diff --git a/DEPS b/DEPS
index c4552768..b777de93 100644
--- a/DEPS
+++ b/DEPS
@@ -96,11 +96,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': 'e7df0bb900ec266021bc6bf4477d533ce1b1f749',
+  'skia_revision': 'a2595f925596aca234d4ac4e35da689ef13cc27c',
   # 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': '9ef8a461214813207c0032d3ad41730adce0539b',
+  'v8_revision': '9c5f77a1382ee3825f708aba48f5675fb55c84e1',
   # 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.
@@ -108,7 +108,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': '3b9b027c59f38580cb038f94d49887e98a80b082',
+  'angle_revision': 'e95a7f077e8bda81cbff51bf84ee80cbf2841e1d',
   # 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.
@@ -120,7 +120,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': 'e5c0fa97c2da104426dbc1cecfc0ed488a22efe5',
+  'pdfium_revision': 'df1298a228abb59eb167d0be43d46a50c0333497',
   # 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.
@@ -156,7 +156,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': '08f785343ab32fb8cc511fdd28d85d417654d94a',
+  'catapult_revision': '718dbe7d32442fbc27b7a150ec2573ab93abea55',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -498,7 +498,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'da3ce5f235a237f27646db55ece4bfdbe04fb832',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9520b5b98bd364e4d0090c92d05c0f8b1eb9cd02',
       'condition': 'checkout_linux',
   },
 
@@ -523,7 +523,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '5ae86d2021277b545889959125e778820849cf15',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3806b7fbd0ea6704d2c35d1eb64a2a505e29ca53',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -653,7 +653,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + 'a9bac57ce6c9d390a52ebaad3259f5fdb871210e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'e4194dc7bbb3305d84cbb1b294274ca70d230721',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'f61e46dbee9d539a32551493e3bcc1dea92f83ec',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -968,7 +968,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c0541da63f571512c49758cbc0767117997a270',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '826738b78c6ab72a0d927339c177fd41848d02df', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + 'd6f86e8fca212e0f1b9b390dd31ef7579b66cc3e', # commit position 21742
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1002,7 +1002,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9ad6814d5f1e026662cac01f07248d671c48b4da',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@59e400635dcd78dd874a63a90fa65300b9945f94',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index b935668..1cf49ac 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -571,7 +571,7 @@
                   '|chrome/browser/resources/settings/',
     },
     'browsing_data': {
-      'filepath': 'chrome/browser/browsing_data/',
+      'filepath': '/browsing_data/',
     },
     'bubble': {
       'filepath': 'ui/views/bubble/|'\
@@ -2180,7 +2180,8 @@
     'nacl': ['native-client-reviews@googlegroups.com'],
     'native_client_sdk': ['binji+watch@chromium.org',
                           'sbc@chromium.org'],
-    'navigation': ['creis+watch@chromium.org',
+    'navigation': ['alexmos+watch@chromium.org',
+                   'creis+watch@chromium.org',
                    'nasko+codewatch@chromium.org'],
     'net': ['cbentzel+watch@chromium.org',
             'net-reviews@chromium.org'],
@@ -2276,6 +2277,7 @@
     'settings_reset_prompt': ['alito+watch@chromium.org'],
     'site_engagement': ['dominickn+watch@chromium.org'],
     'site_instance': ['ajwong+watch@chromium.org',
+                      'alexmos+watch@chromium.org',
                       'creis+watch@chromium.org',
                       'nasko+codewatch@chromium.org'],
     'smb': ['cros-enterprise-lax+smbwatch@chromium.org'],
diff --git a/android_webview/browser/aw_field_trial_creator.cc b/android_webview/browser/aw_field_trial_creator.cc
index 8ff0875..c5e2044 100644
--- a/android_webview/browser/aw_field_trial_creator.cc
+++ b/android_webview/browser/aw_field_trial_creator.cc
@@ -5,6 +5,9 @@
 #include "android_webview/browser/aw_field_trial_creator.h"
 
 #include <memory>
+#include <set>
+#include <string>
+#include <vector>
 
 #include "android_webview/browser/aw_metrics_service_client.h"
 #include "base/base_switches.h"
@@ -27,24 +30,11 @@
 // TODO(kmilka): Update to work properly in environments both with and without
 // UMA enabled.
 std::unique_ptr<const base::FieldTrial::EntropyProvider>
-CreateLowEntropyProvider() {
+CreateLowEntropyProvider(const std::string& client_id) {
   return std::unique_ptr<const base::FieldTrial::EntropyProvider>(
       // Since variations are only enabled for users opted in to UMA, it is
       // acceptable to use the SHA1EntropyProvider for randomization.
-      new variations::SHA1EntropyProvider(
-          // Synchronous read of the client id is permitted as it is fast
-          // enough to have minimal impact on startup time, and is behind the
-          // webview-enable-finch flag.
-          android_webview::AwMetricsServiceClient::GetClientId()));
-}
-
-// Synchronous read of variations data is permitted as it is fast
-// enough to have minimal impact on startup time, and is behind the
-// webview-enable-finch flag.
-bool ReadVariationsSeedDataFromFile(PrefService* local_state) {
-  // TODO(kmilka): The data read is being moved to java and the data will be
-  // passed in via JNI.
-  return false;
+      new variations::SHA1EntropyProvider(client_id));
 }
 
 }  // anonymous namespace
@@ -81,18 +71,19 @@
 }
 
 void AwFieldTrialCreator::SetUpFieldTrials() {
-  AwMetricsServiceClient::LoadOrCreateClientId();
+  // If the client ID isn't available yet, don't delay startup by creating it.
+  // Instead, variations will be disabled for this run.
+  std::string client_id;
+  if (!AwMetricsServiceClient::GetPreloadedClientId(&client_id))
+    return;
 
   DCHECK(!field_trial_list_);
   // Set the FieldTrialList singleton.
-  field_trial_list_ =
-      std::make_unique<base::FieldTrialList>(CreateLowEntropyProvider());
+  field_trial_list_ = std::make_unique<base::FieldTrialList>(
+      CreateLowEntropyProvider(client_id));
 
   std::unique_ptr<PrefService> local_state = CreateLocalState();
 
-  if (!ReadVariationsSeedDataFromFile(local_state.get()))
-    return;
-
   variations::UIStringOverrider ui_string_overrider;
   client_ = std::make_unique<AwVariationsServiceClient>();
   variations_field_trial_creator_ =
@@ -114,7 +105,7 @@
   variations_field_trial_creator_->SetupFieldTrials(
       cc::switches::kEnableGpuBenchmarking, switches::kEnableFeatures,
       switches::kDisableFeatures, unforceable_field_trials,
-      std::vector<std::string>(), CreateLowEntropyProvider(),
+      std::vector<std::string>(), CreateLowEntropyProvider(client_id),
       std::make_unique<base::FeatureList>(), aw_field_trials_.get(),
       &ignored_safe_seed_manager);
 }
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index ea6ccb9..19d8e55f 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -4,10 +4,16 @@
 
 #include "android_webview/browser/aw_metrics_service_client.h"
 
+#include <jni.h>
+#include <stdint.h>
+#include <vector>
+
 #include "android_webview/browser/aw_metrics_log_uploader.h"
 #include "android_webview/common/aw_switches.h"
 #include "android_webview/jni/AwMetricsServiceClient_jni.h"
 #include "base/android/build_info.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -41,8 +47,10 @@
 
 const int kUploadIntervalMinutes = 30;
 
-// A GUID in text form is composed of 32 hex digits and 4 hyphens.
-const size_t GUID_SIZE = 32 + 4;
+// A GUID in text form is composed of 32 hex digits and 4 hyphens. These values
+// must match those in AwMetricsServiceClient.java.
+const size_t kGuidSize = 32 + 4;
+const char* const kGuidFileName = "metrics_guid";
 
 // Client ID of the app, read and cached synchronously at startup
 base::LazyInstance<std::string>::Leaky g_client_id = LAZY_INSTANCE_INITIALIZER;
@@ -79,9 +87,22 @@
   return g_lazy_instance_.Pointer();
 }
 
+bool AwMetricsServiceClient::GetPreloadedClientId(std::string* client_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jbyteArray> client_id_java =
+      Java_AwMetricsServiceClient_getPreloadedClientId(env);
+  if (client_id_java.is_null())
+    return false;
+  std::vector<uint8_t> client_id_vector;
+  base::android::JavaByteArrayToByteVector(env, client_id_java.obj(),
+                                           &client_id_vector);
+  *client_id = std::string(client_id_vector.begin(), client_id_vector.end());
+  return true;
+}
+
 void AwMetricsServiceClient::LoadOrCreateClientId() {
   // This function should only be called once at start up.
-  DCHECK_NE(g_client_id.Get().length(), GUID_SIZE);
+  DCHECK_NE(g_client_id.Get().length(), kGuidSize);
 
   // UMA uses randomly-generated GUIDs (globally unique identifiers) to
   // anonymously identify logs. Every WebView-using app on every device
@@ -96,11 +117,12 @@
   }
 
   const base::FilePath guid_file_path =
-      user_data_dir.Append(FILE_PATH_LITERAL("metrics_guid"));
+      user_data_dir.Append(FILE_PATH_LITERAL(kGuidFileName));
 
-  // Try to read an existing GUID.
-  if (base::ReadFileToStringWithMaxSize(guid_file_path, &g_client_id.Get(),
-                                        GUID_SIZE)) {
+  // Try to get an existing GUID.
+  if (GetPreloadedClientId(&g_client_id.Get()) ||
+      base::ReadFileToStringWithMaxSize(guid_file_path, &g_client_id.Get(),
+                                        kGuidSize)) {
     if (base::IsValidGUID(g_client_id.Get()))
       return;
     LOG(ERROR) << "Overwriting invalid GUID";
@@ -119,7 +141,7 @@
 std::string AwMetricsServiceClient::GetClientId() {
   // This function should only be called if LoadOrCreateClientId() was
   // previously called.
-  DCHECK_EQ(g_client_id.Get().length(), GUID_SIZE);
+  DCHECK_EQ(g_client_id.Get().length(), kGuidSize);
 
   return g_client_id.Get();
 }
@@ -134,24 +156,17 @@
   pref_service_ = pref_service;
   request_context_ = request_context;
 
-  // If variations are enabled for WebView the GUID will already have been read
-  // at startup
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableWebViewVariations)) {
-    InitializeWithClientId();
-  } else {
-    base::PostTaskWithTraitsAndReply(
-        FROM_HERE, {base::MayBlock()},
-        base::BindOnce(&AwMetricsServiceClient::LoadOrCreateClientId),
-        base::BindOnce(&AwMetricsServiceClient::InitializeWithClientId,
-                       base::Unretained(this)));
-  }
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&AwMetricsServiceClient::LoadOrCreateClientId),
+      base::BindOnce(&AwMetricsServiceClient::InitializeWithClientId,
+                     base::Unretained(this)));
 }
 
 void AwMetricsServiceClient::InitializeWithClientId() {
   // The guid must have already been initialized at this point, either
   // synchronously or asynchronously depending on the kEnableWebViewFinch flag
-  DCHECK_EQ(g_client_id.Get().length(), GUID_SIZE);
+  DCHECK_EQ(g_client_id.Get().length(), kGuidSize);
   pref_service_->SetString(metrics::prefs::kMetricsClientID, g_client_id.Get());
 
   in_sample_ = IsInSample(g_client_id.Get());
diff --git a/android_webview/browser/aw_metrics_service_client.h b/android_webview/browser/aw_metrics_service_client.h
index a850a91..5880206 100644
--- a/android_webview/browser/aw_metrics_service_client.h
+++ b/android_webview/browser/aw_metrics_service_client.h
@@ -44,6 +44,10 @@
  public:
   static AwMetricsServiceClient* GetInstance();
 
+  // If the client ID was pre-loaded on the Java side, store it in "client_id"
+  // and return true; otherwise, return false.
+  static bool GetPreloadedClientId(std::string* client_id);
+
   // Retrieve the client ID or generate one if none exists.
   static void LoadOrCreateClientId();
 
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 aabb08a0..0f16254 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
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.print.PrintDocumentAdapter;
 import android.util.Log;
 import android.util.SparseArray;
@@ -62,6 +63,7 @@
 import org.chromium.android_webview.renderer_priority.RendererPriority;
 import org.chromium.base.BuildInfo;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample;
 import org.chromium.components.autofill.AutofillProvider;
 import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.content_public.browser.SmartClipProvider;
@@ -72,6 +74,7 @@
 import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 /**
  * This class is the delegate to which WebViewProxy forwards all API calls.
@@ -149,6 +152,8 @@
     // so is ignored. TODO: remove it from WebViewProvider.
     public void init(final Map<String, Object> javaScriptInterfaces,
             final boolean privateBrowsing) {
+        long startTime = SystemClock.elapsedRealtime();
+        boolean isFirstWebViewInit = !mFactory.hasStarted();
         try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("WebViewChromium.init")) {
             if (privateBrowsing) {
                 mFactory.startYourEngines(true);
@@ -170,7 +175,10 @@
             //   comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
             if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                 mFactory.startYourEngines(false);
-                checkThread();
+                try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
+                             "WebViewChromium.checkThreadInsideInit")) {
+                    checkThread();
+                }
             } else if (!mFactory.hasStarted()) {
                 if (Looper.myLooper() == Looper.getMainLooper()) {
                     mFactory.startYourEngines(true);
@@ -190,8 +198,11 @@
             final boolean doNotUpdateSelectionOnMutatingSelectionRange =
                     mAppTargetSdkVersion <= Build.VERSION_CODES.M;
 
-            mContentsClientAdapter =
-                    mFactory.createWebViewContentsClientAdapter(mWebView, mContext);
+            try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped(
+                         "WebViewChromiumFactoryProvider.createWebViewContentsClientAdapter")) {
+                mContentsClientAdapter =
+                        mFactory.createWebViewContentsClientAdapter(mWebView, mContext);
+            }
             try (ScopedSysTraceEvent e2 =
                             ScopedSysTraceEvent.scoped("WebViewChromium.ContentSettingsAdapter")) {
                 mWebSettings = new ContentSettingsAdapter(new AwSettings(mContext,
@@ -227,6 +238,15 @@
                     }
                 }
             });
+        } finally {
+            // The real initialization may be deferred, in which case we don't record this.
+            if (!mFactory.hasStarted()) return;
+
+            TimesHistogramSample histogram = new TimesHistogramSample(
+                    "Android.WebView.Startup.CreationTime.Stage2.ProviderInit."
+                            + (isFirstWebViewInit ? "Cold" : "Warm"),
+                    TimeUnit.MILLISECONDS);
+            histogram.record(SystemClock.elapsedRealtime() - startTime);
         }
     }
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 883afe5..6d11e32 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -12,6 +12,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
@@ -44,6 +45,7 @@
 import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.NativeLibraries;
+import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample;
 import org.chromium.components.autofill.AutofillProvider;
 import org.chromium.content.browser.selection.LGEmailActionModeWorkaround;
 
@@ -51,6 +53,7 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Entry point to the WebView. The system framework talks to this class to get instances of the
@@ -163,6 +166,7 @@
 
     @TargetApi(Build.VERSION_CODES.N) // For getSystemService() and isUserUnlocked().
     private void initialize(WebViewDelegate webViewDelegate) {
+        long startTime = SystemClock.elapsedRealtime();
         try (ScopedSysTraceEvent e1 =
                         ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.initialize")) {
             // The package is used to locate the services for copying crash minidumps and requesting
@@ -253,6 +257,11 @@
                     shouldDisableThreadChecking(ContextUtils.getApplicationContext());
 
             setSingleton(this);
+        } finally {
+            TimesHistogramSample histogram = new TimesHistogramSample(
+                    "Android.WebView.Startup.CreationTime.Stage1.FactoryInit",
+                    TimeUnit.MILLISECONDS);
+            histogram.record(SystemClock.elapsedRealtime() - startTime);
         }
     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
index d2f22eb..0e168e26 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
@@ -9,10 +9,15 @@
 import android.content.pm.PackageManager;
 
 import org.chromium.base.Log;
+import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
 /**
  * Determines user consent and app opt-out for metrics.
  *
@@ -26,6 +31,11 @@
  * This happens in parallel with native AwMetricsServiceClient initialization; either
  * nativeInitialized or setConsentSetting might fire first. Whichever fires second should call
  * nativeSetHaveMetricsConsent.
+ *
+ * Also, pre-loads the client ID for variations. Variations setup begins before native init (so it
+ * can't use the native code to load the client ID), reading the client ID and other variations
+ * files in the background. Completion of this task blocks WebView startup, so we want to do minimal
+ * work; unlike the native loader, this loader does not create a new client ID when none is found.
  */
 @JNINamespace("android_webview")
 public class AwMetricsServiceClient {
@@ -38,6 +48,60 @@
     private static boolean sIsClientReady; // Is the native AwMetricsServiceClient initialized?
     private static boolean sShouldEnable; // Have steps 1 and 2 passed?
 
+    // A GUID in text form is composed of 32 hex digits and 4 hyphens. These values must match those
+    // in aw_metrics_service_client.cc.
+    private static final int GUID_SIZE = 32 + 4;
+    private static final String GUID_FILE_NAME = "metrics_guid";
+
+    private static byte[] sClientId;
+    private static Object sClientIdLock = new Object();
+
+    private static byte[] readFixedLengthFile(File file, int length) throws IOException {
+        if (file.length() != length) {
+            throw new IOException("File is not of expected length " + length);
+        }
+        FileInputStream in = null;
+        try {
+            in = new FileInputStream(file);
+            byte[] buf = new byte[length];
+            int read = 0;
+            int offset = 0;
+            while (offset < length) {
+                read = in.read(buf, offset, length - offset);
+                if (read < 1) throw new IOException("Premature EOF");
+                offset += read;
+            }
+            return buf;
+        } finally {
+            if (in != null) in.close();
+        }
+    }
+
+    /**
+     * Load the metrics client ID, if any.
+     */
+    public static void preloadClientId() {
+        File clientIdFile = new File(PathUtils.getDataDirectory(), GUID_FILE_NAME);
+        if (!clientIdFile.exists() || clientIdFile.length() != GUID_SIZE) return;
+        byte[] clientId = null;
+        try {
+            clientId = readFixedLengthFile(clientIdFile, GUID_SIZE);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to pre-load GUID file " + clientIdFile + " - " + e.getMessage());
+            return;
+        }
+        synchronized (sClientIdLock) {
+            sClientId = clientId;
+        }
+    }
+
+    @CalledByNative
+    public static byte[] getPreloadedClientId() {
+        synchronized (sClientIdLock) {
+            return sClientId;
+        }
+    }
+
     private static boolean isAppOptedOut(Context appContext) {
         try {
             ApplicationInfo info = appContext.getPackageManager().getApplicationInfo(
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
index 00616a2..adee16ce 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -44,12 +44,14 @@
  * by the Runnable.
  *
  * The Runnable and FutureTask together perform these steps:
- * 1. Load the new seed file, if any.
- * 2. If no new seed file, load the old seed file, if any.
- * 3. Make the loaded seed available via get() (or null if there was no seed).
- * 4. If there was a new seed file, replace the old with the new (but only after making the loaded
+ * 1. Pre-load the metrics client ID. This is needed to seed the EntropyProvider. If there is no
+ *    client ID, variations can't be used on this run.
+ * 2. Load the new seed file, if any.
+ * 3. If no new seed file, load the old seed file, if any.
+ * 4. Make the loaded seed available via get() (or null if there was no seed).
+ * 5. If there was a new seed file, replace the old with the new (but only after making the loaded
  *    seed available, as the replace need not block startup).
- * 5. If there was no seed, or the loaded seed was expired, request a new seed (but don't request
+ * 6. If there was no seed, or the loaded seed was expired, request a new seed (but don't request
  *    more often than MAX_REQUEST_PERIOD_MILLIS).
  *
  * VariationsSeedLoader should be used during WebView startup like so:
@@ -76,6 +78,10 @@
     // is exceeded, proceed with variations disabled.
     private static final long SEED_LOAD_TIMEOUT_MILLIS = 20;
 
+    // If false, then variations will be enabled if either the CMD flag or the AGSA experiment file
+    // is present. If true, then variations will be enabled regardless of flag or experiment file.
+    private static boolean sVariationsAlwaysEnabled = false;
+
     private static void recordLoadSeedResult(int result) {
         EnumeratedHistogramSample histogram = new EnumeratedHistogramSample(
                 "Variations.SeedLoadResult", LoadSeedResult.ENUM_SIZE);
@@ -121,6 +127,8 @@
             mEnabledByExperiment = checkEnabledByExperiment();
             if (!(mEnabledByCmd || mEnabledByExperiment)) return null;
 
+            AwMetricsServiceClient.preloadClientId();
+
             File newSeedFile = VariationsUtils.getNewSeedFile();
             File oldSeedFile = VariationsUtils.getSeedFile();
 
@@ -208,7 +216,7 @@
         // mEnabledByExperiment is set in mLoadTask, so isVariationsEnabled() should only be called
         // after run() returns.
         public boolean isVariationsEnabled() {
-            return mEnabledByCmd || mEnabledByExperiment;
+            return sVariationsAlwaysEnabled || mEnabledByCmd || mEnabledByExperiment;
         }
     }
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 62da806..78c8f02 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -227,6 +227,8 @@
     "frame/caption_buttons/frame_size_button.cc",
     "frame/caption_buttons/frame_size_button.h",
     "frame/caption_buttons/frame_size_button_delegate.h",
+    "frame/custom_frame_header.cc",
+    "frame/custom_frame_header.h",
     "frame/custom_frame_view_ash.cc",
     "frame/custom_frame_view_ash.h",
     "frame/default_frame_header.cc",
@@ -603,6 +605,16 @@
     "system/locale/locale_notification_controller.h",
     "system/media_security/multi_profile_media_tray_item.cc",
     "system/media_security/multi_profile_media_tray_item.h",
+    "system/message_center/ash_popup_alignment_delegate.cc",
+    "system/message_center/ash_popup_alignment_delegate.h",
+    "system/message_center/fullscreen_notification_blocker.cc",
+    "system/message_center/fullscreen_notification_blocker.h",
+    "system/message_center/inactive_user_notification_blocker.cc",
+    "system/message_center/inactive_user_notification_blocker.h",
+    "system/message_center/notification_tray.cc",
+    "system/message_center/notification_tray.h",
+    "system/message_center/session_state_notification_blocker.cc",
+    "system/message_center/session_state_notification_blocker.h",
     "system/model/clock_model.cc",
     "system/model/clock_model.h",
     "system/model/enterprise_domain_model.cc",
@@ -780,6 +792,8 @@
     "system/tray/actionable_view.h",
     "system/tray/hover_highlight_view.cc",
     "system/tray/hover_highlight_view.h",
+    "system/tray/interacted_by_tap_recorder.cc",
+    "system/tray/interacted_by_tap_recorder.h",
     "system/tray/label_tray_view.cc",
     "system/tray/label_tray_view.h",
     "system/tray/size_range_layout.cc",
@@ -889,16 +903,6 @@
     "system/virtual_keyboard/virtual_keyboard_observer.h",
     "system/virtual_keyboard/virtual_keyboard_tray.cc",
     "system/virtual_keyboard/virtual_keyboard_tray.h",
-    "system/web_notification/ash_popup_alignment_delegate.cc",
-    "system/web_notification/ash_popup_alignment_delegate.h",
-    "system/web_notification/fullscreen_notification_blocker.cc",
-    "system/web_notification/fullscreen_notification_blocker.h",
-    "system/web_notification/inactive_user_notification_blocker.cc",
-    "system/web_notification/inactive_user_notification_blocker.h",
-    "system/web_notification/session_state_notification_blocker.cc",
-    "system/web_notification/session_state_notification_blocker.h",
-    "system/web_notification/web_notification_tray.cc",
-    "system/web_notification/web_notification_tray.h",
     "touch/ash_touch_transform_controller.cc",
     "touch/ash_touch_transform_controller.h",
     "touch/touch_devices_controller.cc",
@@ -1078,6 +1082,8 @@
     "wm/splitview/split_view_divider.h",
     "wm/splitview/split_view_drag_indicators.cc",
     "wm/splitview/split_view_drag_indicators.h",
+    "wm/splitview/split_view_highlight_view.cc",
+    "wm/splitview/split_view_highlight_view.h",
     "wm/splitview/split_view_utils.cc",
     "wm/splitview/split_view_utils.h",
     "wm/stacking_controller.cc",
@@ -1556,6 +1562,7 @@
     "app_list/app_list_presenter_delegate_unittest.cc",
     "app_list/model/app_list_item_list_unittest.cc",
     "app_list/model/app_list_model_unittest.cc",
+    "assistant/ash_assistant_controller_unittest.cc",
     "autoclick/autoclick_unittest.cc",
     "detachable_base/detachable_base_handler_unittest.cc",
     "detachable_base/detachable_base_notification_controller_unittest.cc",
@@ -1679,6 +1686,10 @@
     "system/ime_menu/ime_menu_tray_unittest.cc",
     "system/keyboard_brightness/tray_keyboard_brightness_unittest.cc",
     "system/media_security/multi_profile_media_tray_item_unittest.cc",
+    "system/message_center/ash_popup_alignment_delegate_unittest.cc",
+    "system/message_center/inactive_user_notification_blocker_unittest.cc",
+    "system/message_center/notification_tray_unittest.cc",
+    "system/message_center/session_state_notification_blocker_unittest.cc",
     "system/network/network_icon_unittest.cc",
     "system/network/sms_observer_unittest.cc",
     "system/network/tray_network_unittest.cc",
@@ -1735,10 +1746,6 @@
     "system/unified/unified_system_info_view_unittest.cc",
     "system/update/tray_update_unittest.cc",
     "system/user/tray_user_unittest.cc",
-    "system/web_notification/ash_popup_alignment_delegate_unittest.cc",
-    "system/web_notification/inactive_user_notification_blocker_unittest.cc",
-    "system/web_notification/session_state_notification_blocker_unittest.cc",
-    "system/web_notification/web_notification_tray_unittest.cc",
     "test/ash_test_helper_unittest.cc",
     "test/ash_unittests.cc",
     "tooltips/tooltip_controller_unittest.cc",
@@ -1780,6 +1787,7 @@
     "wm/screen_pinning_controller_unittest.cc",
     "wm/session_state_animator_impl_unittest.cc",
     "wm/splitview/split_view_controller_unittest.cc",
+    "wm/splitview/split_view_highlight_view_unittest.cc",
     "wm/stacking_controller_unittest.cc",
     "wm/system_gesture_event_filter_unittest.cc",
     "wm/system_modal_container_layout_manager_unittest.cc",
@@ -2088,6 +2096,8 @@
     "wm/cursor_manager_test_api.h",
     "wm/lock_state_controller_test_api.cc",
     "wm/lock_state_controller_test_api.h",
+    "wm/splitview/split_view_highlight_view_test_api.cc",
+    "wm/splitview/split_view_highlight_view_test_api.h",
     "wm/test_activation_delegate.cc",
     "wm/test_activation_delegate.h",
     "wm/test_child_modal_parent.cc",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 01ece8fb..12beafa 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -40,6 +40,7 @@
 #include "ash/system/brightness_control_delegate.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
 #include "ash/system/keyboard_brightness_control_delegate.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/palette/palette_tray.h"
 #include "ash/system/palette/palette_utils.h"
 #include "ash/system/power/power_button_controller.h"
@@ -49,7 +50,6 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/unified/unified_system_tray.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/touch/touch_hud_debug.h"
 #include "ash/utility/screenshot_controller.h"
 #include "ash/voice_interaction/voice_interaction_controller.h"
@@ -409,7 +409,7 @@
   StatusAreaWidget* status_area_widget =
       Shelf::ForWindow(target_root)->shelf_widget()->status_area_widget();
   return status_area_widget &&
-         status_area_widget->web_notification_tray()->visible();
+         status_area_widget->notification_tray()->visible();
 }
 
 void HandleToggleMessageCenterBubble() {
@@ -419,8 +419,7 @@
       Shelf::ForWindow(target_root)->shelf_widget()->status_area_widget();
   if (!status_area_widget)
     return;
-  WebNotificationTray* notification_tray =
-      status_area_widget->web_notification_tray();
+  NotificationTray* notification_tray = status_area_widget->notification_tray();
   if (!notification_tray->visible())
     return;
   if (notification_tray->IsMessageCenterVisible())
diff --git a/ash/accessibility/key_accessibility_enabler_unittest.cc b/ash/accessibility/key_accessibility_enabler_unittest.cc
index 055c960..42146119 100644
--- a/ash/accessibility/key_accessibility_enabler_unittest.cc
+++ b/ash/accessibility/key_accessibility_enabler_unittest.cc
@@ -7,7 +7,7 @@
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/shell.h"
-#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/run_loop.h"
@@ -23,7 +23,7 @@
   KeyAccessibilityEnablerTest() {}
 
   void SetUp() override {
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
     ui::SetEventTickClockForTesting(&clock_);
     AshTestBase::SetUp();
     Shell::Get()->accessibility_controller()->AddObserver(this);
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 7fb4960..971b7451 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -750,7 +750,7 @@
       </message>
 
       <message name="IDS_AURA_SET_DESKTOP_WALLPAPER" desc="The label used for change wallpaper in context menu">
-        Set wallpaper...
+        Set wallpaper
       </message>
 
       <message name="IDS_ASH_STATUS_TRAY_TRACING" desc="The status tray item indicating that performance tracing is running.">
diff --git a/ash/assistant/ash_assistant_controller.cc b/ash/assistant/ash_assistant_controller.cc
index 005aaf5..2fc222ed 100644
--- a/ash/assistant/ash_assistant_controller.cc
+++ b/ash/assistant/ash_assistant_controller.cc
@@ -25,9 +25,11 @@
       assistant_event_subscriber_binding_(this),
       assistant_bubble_(std::make_unique<AssistantBubble>(this)) {
   AddInteractionModelObserver(this);
+  Shell::Get()->highlighter_controller()->AddObserver(this);
 }
 
 AshAssistantController::~AshAssistantController() {
+  Shell::Get()->highlighter_controller()->RemoveObserver(this);
   RemoveInteractionModelObserver(this);
 
   assistant_controller_binding_.Close();
@@ -133,6 +135,15 @@
   }
 }
 
+void AshAssistantController::OnHighlighterEnabledChanged(bool enabled) {
+  // TODO(warx): add a reason enum to distinguish the case of deselecting the
+  // tool and done with a stylus selection.
+  assistant_bubble_timer_.Stop();
+  assistant_interaction_model_.SetInputModality(InputModality::kStylus);
+  assistant_interaction_model_.SetInteractionState(
+      enabled ? InteractionState::kActive : InteractionState::kInactive);
+}
+
 void AshAssistantController::OnInteractionStarted() {
   assistant_bubble_timer_.Stop();
   assistant_interaction_model_.SetInteractionState(InteractionState::kActive);
diff --git a/ash/assistant/ash_assistant_controller.h b/ash/assistant/ash_assistant_controller.h
index 6e6f5c1..e21df13 100644
--- a/ash/assistant/ash_assistant_controller.h
+++ b/ash/assistant/ash_assistant_controller.h
@@ -11,6 +11,7 @@
 
 #include "ash/assistant/model/assistant_interaction_model.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
+#include "ash/highlighter/highlighter_controller.h"
 #include "ash/public/interfaces/ash_assistant_controller.mojom.h"
 #include "ash/public/interfaces/assistant_card_renderer.mojom.h"
 #include "base/macros.h"
@@ -30,7 +31,8 @@
 class AshAssistantController
     : public mojom::AshAssistantController,
       public chromeos::assistant::mojom::AssistantEventSubscriber,
-      public AssistantInteractionModelObserver {
+      public AssistantInteractionModelObserver,
+      public HighlighterController::Observer {
  public:
   AshAssistantController();
   ~AshAssistantController() override;
@@ -81,6 +83,9 @@
   // AssistantInteractionModelObserver:
   void OnInteractionStateChanged(InteractionState interaction_state) override;
 
+  // HighlighterController::Observer:
+  void OnHighlighterEnabledChanged(bool enabled) override;
+
   // chromeos::assistant::mojom::AssistantEventSubscriber:
   void OnInteractionStarted() override;
   void OnInteractionFinished(
diff --git a/ash/assistant/ash_assistant_controller_unittest.cc b/ash/assistant/ash_assistant_controller_unittest.cc
new file mode 100644
index 0000000..5d0c25e
--- /dev/null
+++ b/ash/assistant/ash_assistant_controller_unittest.cc
@@ -0,0 +1,54 @@
+// 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/assistant/ash_assistant_controller.h"
+
+#include "ash/highlighter/highlighter_controller.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/chromeos_switches.h"
+
+namespace ash {
+
+class AshAssistantControllerTest : public AshTestBase {
+ protected:
+  AshAssistantControllerTest() = default;
+  ~AshAssistantControllerTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        chromeos::switches::kAssistantFeature);
+    ASSERT_TRUE(chromeos::switches::IsAssistantEnabled());
+    AshTestBase::SetUp();
+    controller_ = Shell::Get()->ash_assistant_controller();
+    DCHECK(controller_);
+  }
+
+  const AssistantInteractionModel* interaction_model() {
+    return controller_->interaction_model();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  AshAssistantController* controller_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AshAssistantControllerTest);
+};
+
+TEST_F(AshAssistantControllerTest, HighlighterEnabledStatus) {
+  HighlighterController* highlighter_controller =
+      Shell::Get()->highlighter_controller();
+  highlighter_controller->SetEnabled(true);
+  EXPECT_EQ(InputModality::kStylus, interaction_model()->input_modality());
+  EXPECT_EQ(InteractionState::kActive,
+            interaction_model()->interaction_state());
+
+  highlighter_controller->SetEnabled(false);
+  EXPECT_EQ(InteractionState::kInactive,
+            interaction_model()->interaction_state());
+}
+
+}  // namespace ash
diff --git a/ash/assistant/ui/assistant_bubble.cc b/ash/assistant/ui/assistant_bubble.cc
index 5ca6f36..d6f3757 100644
--- a/ash/assistant/ui/assistant_bubble.cc
+++ b/ash/assistant/ui/assistant_bubble.cc
@@ -69,10 +69,17 @@
 
   int GetDialogButtons() const override { return ui::DIALOG_BUTTON_NONE; }
 
+  void RequestFocus() override {
+    if (assistant_bubble_view_)
+      assistant_bubble_view_->RequestFocus();
+  }
+
  private:
   void InitLayout() {
     SetLayoutManager(std::make_unique<views::FillLayout>());
-    AddChildView(new AssistantBubbleView(assistant_controller_));
+
+    assistant_bubble_view_ = new AssistantBubbleView(assistant_controller_);
+    AddChildView(assistant_bubble_view_);
   }
 
   void SetAnchor() {
@@ -89,7 +96,10 @@
     SetAnchorRect(anchor);
   }
 
-  AshAssistantController* assistant_controller_;  // Owned by Shell.
+  AshAssistantController* const assistant_controller_;  // Owned by Shell.
+
+  // Owned by view hierarchy.
+  AssistantBubbleView* assistant_bubble_view_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantContainerView);
 };
@@ -110,8 +120,14 @@
     container_view_->GetWidget()->RemoveObserver(this);
 }
 
-void AssistantBubble::OnWidgetClosing(views::Widget* widget) {
-  widget->RemoveObserver(this);
+void AssistantBubble::OnWidgetActivationChanged(views::Widget* widget,
+                                                bool active) {
+  if (active)
+    container_view_->RequestFocus();
+}
+
+void AssistantBubble::OnWidgetDestroying(views::Widget* widget) {
+  container_view_->GetWidget()->RemoveObserver(this);
   container_view_ = nullptr;
 }
 
@@ -128,16 +144,16 @@
 }
 
 void AssistantBubble::Show() {
-  if (!container_view_)
+  if (!container_view_) {
     container_view_ = new AssistantContainerView(assistant_controller_);
-
-  container_view_->GetWidget()->AddObserver(this);
-  container_view_->GetWidget()->ShowInactive();
+    container_view_->GetWidget()->AddObserver(this);
+  }
+  container_view_->GetWidget()->Show();
 }
 
 void AssistantBubble::Dismiss() {
   if (container_view_)
-    container_view_->GetWidget()->Close();
+    container_view_->GetWidget()->Hide();
 }
 
 }  // namespace ash
diff --git a/ash/assistant/ui/assistant_bubble.h b/ash/assistant/ui/assistant_bubble.h
index 9830200..e7ddccb 100644
--- a/ash/assistant/ui/assistant_bubble.h
+++ b/ash/assistant/ui/assistant_bubble.h
@@ -28,7 +28,8 @@
   ~AssistantBubble() override;
 
   // views::WidgetObserver:
-  void OnWidgetClosing(views::Widget* widget) override;
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+  void OnWidgetDestroying(views::Widget* widget) override;
 
   // AssistantInteractionModelObserver:
   void OnInteractionStateChanged(InteractionState interaction_state) override;
diff --git a/ash/assistant/ui/assistant_bubble_view.cc b/ash/assistant/ui/assistant_bubble_view.cc
index 7c2fa3a..f00c2fd 100644
--- a/ash/assistant/ui/assistant_bubble_view.cc
+++ b/ash/assistant/ui/assistant_bubble_view.cc
@@ -360,6 +360,7 @@
           new InteractionContainer(assistant_controller->interaction_model())),
       ui_element_container_(new UiElementContainer()),
       suggestions_container_(new SuggestionsContainer(this)),
+      dialog_plate_(new DialogPlate(assistant_controller)),
       render_request_weak_factory_(this) {
   InitLayout();
 
@@ -384,13 +385,26 @@
 }
 
 void AssistantBubbleView::ChildVisibilityChanged(views::View* child) {
+  // When toggling the visibility of the dialog plate, we also need to update
+  // the bottom padding of the layout.
+  if (child == dialog_plate_) {
+    const int padding_bottom_dip = dialog_plate_->visible() ? 0 : kPaddingDip;
+    layout_manager_->set_inside_border_insets(
+        gfx::Insets(kPaddingDip, 0, padding_bottom_dip, 0));
+  }
   PreferredSizeChanged();
 }
 
 void AssistantBubbleView::InitLayout() {
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
+  // Dialog plate is not visible when using the stylus input modality.
+  const bool show_dialog_plate =
+      assistant_controller_->interaction_model()->input_modality() !=
+      InputModality::kStylus;
+
+  layout_manager_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
-      gfx::Insets(kPaddingDip, 0, 0, 0), kSpacingDip));
+      gfx::Insets(kPaddingDip, 0, show_dialog_plate ? 0 : kPaddingDip, 0),
+      kSpacingDip));
 
   // Interaction container.
   AddChildView(interaction_container_);
@@ -404,7 +418,8 @@
   AddChildView(suggestions_container_);
 
   // Dialog plate.
-  AddChildView(new DialogPlate(assistant_controller_));
+  dialog_plate_->SetVisible(show_dialog_plate);
+  AddChildView(dialog_plate_);
 }
 
 void AssistantBubbleView::SetProcessingUiElement(bool is_processing) {
@@ -429,6 +444,9 @@
 }
 
 void AssistantBubbleView::OnInputModalityChanged(InputModality input_modality) {
+  // Dialog plate is not visible when using stylus input modality.
+  dialog_plate_->SetVisible(input_modality != InputModality::kStylus);
+
   // If the query for the interaction is empty, we may need to update the prompt
   // to reflect the current input modality.
   if (assistant_controller_->interaction_model()->query().empty()) {
@@ -553,4 +571,8 @@
   ui_element_container_->SetVisible(true);
 }
 
+void AssistantBubbleView::RequestFocus() {
+  dialog_plate_->RequestFocus();
+}
+
 }  // namespace ash
diff --git a/ash/assistant/ui/assistant_bubble_view.h b/ash/assistant/ui/assistant_bubble_view.h
index 80013522..e288a315 100644
--- a/ash/assistant/ui/assistant_bubble_view.h
+++ b/ash/assistant/ui/assistant_bubble_view.h
@@ -15,12 +15,17 @@
 #include "ui/app_list/views/suggestion_chip_view.h"
 #include "ui/views/view.h"
 
+namespace views {
+class BoxLayout;
+}  // namespace views
+
 namespace ash {
 
 class AshAssistantController;
 class AssistantCardElement;
 class AssistantTextElement;
 class AssistantUiElement;
+class DialogPlate;
 
 namespace {
 class InteractionContainer;
@@ -39,6 +44,7 @@
   gfx::Size CalculatePreferredSize() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void ChildVisibilityChanged(views::View* child) override;
+  void RequestFocus() override;
 
   // AssistantInteractionModelObserver:
   void OnInputModalityChanged(InputModality input_modality) override;
@@ -73,6 +79,9 @@
   InteractionContainer* interaction_container_;  // Owned by view hierarchy.
   UiElementContainer* ui_element_container_;     // Owned by view hierarchy.
   SuggestionsContainer* suggestions_container_;  // Owned by view hierarchy.
+  DialogPlate* dialog_plate_;                    // Owned by view hierarchy.
+
+  views::BoxLayout* layout_manager_ = nullptr;  // Owned by view hierarchy.
 
   // Uniquely identifies cards owned by AssistantCardRenderer.
   std::vector<base::UnguessableToken> id_token_list_;
diff --git a/ash/assistant/ui/dialog_plate.cc b/ash/assistant/ui/dialog_plate.cc
index 177636d0..b70c9b5 100644
--- a/ash/assistant/ui/dialog_plate.cc
+++ b/ash/assistant/ui/dialog_plate.cc
@@ -68,7 +68,9 @@
 // DialogPlate -----------------------------------------------------------------
 
 DialogPlate::DialogPlate(AshAssistantController* assistant_controller)
-    : assistant_controller_(assistant_controller), icon_(new views::View()) {
+    : assistant_controller_(assistant_controller),
+      textfield_(new views::Textfield()),
+      icon_(new views::View()) {
   InitLayout();
 
   // The Assistant controller indirectly owns the view hierarchy to which
@@ -93,18 +95,17 @@
       views::Textfield::GetDefaultFontList().DeriveWithSizeDelta(4);
 
   // Textfield.
-  views::Textfield* textfield = new views::Textfield();
-  textfield->SetBackgroundColor(SK_ColorTRANSPARENT);
-  textfield->SetBorder(views::NullBorder());
-  textfield->set_controller(this);
-  textfield->SetFontList(font_list);
-  textfield->set_placeholder_font_list(font_list);
-  textfield->set_placeholder_text(base::UTF8ToUTF16(kHint));
-  textfield->set_placeholder_text_color(kTextColorHint);
-  textfield->SetTextColor(kTextColorPrimary);
-  AddChildView(textfield);
+  textfield_->SetBackgroundColor(SK_ColorTRANSPARENT);
+  textfield_->SetBorder(views::NullBorder());
+  textfield_->set_controller(this);
+  textfield_->SetFontList(font_list);
+  textfield_->set_placeholder_font_list(font_list);
+  textfield_->set_placeholder_text(base::UTF8ToUTF16(kHint));
+  textfield_->set_placeholder_text_color(kTextColorHint);
+  textfield_->SetTextColor(kTextColorPrimary);
+  AddChildView(textfield_);
 
-  layout->SetFlexForView(textfield, 1);
+  layout->SetFlexForView(textfield_, 1);
 
   // TODO(dmblack): Replace w/ stateful icon.
   // Icon.
@@ -186,4 +187,8 @@
   SchedulePaint();
 }
 
+void DialogPlate::RequestFocus() {
+  textfield_->RequestFocus();
+}
+
 }  // namespace ash
diff --git a/ash/assistant/ui/dialog_plate.h b/ash/assistant/ui/dialog_plate.h
index b550a54..8d2a793 100644
--- a/ash/assistant/ui/dialog_plate.h
+++ b/ash/assistant/ui/dialog_plate.h
@@ -21,6 +21,9 @@
   explicit DialogPlate(AshAssistantController* assistant_controller);
   ~DialogPlate() override;
 
+  // views::View:
+  void RequestFocus() override;
+
   // AssistantInteractionModelObserver:
   void OnInputModalityChanged(InputModality input_modality) override;
   void OnMicStateChanged(MicState mic_state) override;
@@ -36,6 +39,7 @@
   void UpdateIcon();
 
   AshAssistantController* const assistant_controller_;  // Owned by Shell.
+  views::Textfield* textfield_;  // Owned by view hierarchy.
   views::View* icon_;  // Owned by view hierarchy.
 
   DISALLOW_COPY_AND_ASSIGN(DialogPlate);
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc
index 451c544..20af98b 100644
--- a/ash/display/cros_display_config.cc
+++ b/ash/display/cros_display_config.cc
@@ -292,8 +292,10 @@
 
   info->display_zoom_factor = display_info.zoom_factor();
   if (has_active_mode) {
-    info->available_display_zoom_factors =
-        display::GetDisplayZoomFactors(active_mode);
+    const auto zoom_levels = display::GetDisplayZoomFactors(active_mode);
+    info->available_display_zoom_factors.assign(zoom_levels.begin(),
+                                                zoom_levels.end());
+
   } else {
     info->available_display_zoom_factors.push_back(display_info.zoom_factor());
   }
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 008efbd..932818a 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -1403,7 +1403,7 @@
   display_manager()->UpdateDisplays();
 
   // Enumerate the zoom factors for display.
-  const std::vector<double> zoom_factors_1 =
+  const std::vector<float> zoom_factors_1 =
       display::GetDisplayZoomFactors(modes_1[0]);
 
   // Set the zoom factor to one of the enumerated zoom factors for the said
@@ -1413,36 +1413,36 @@
                                       zoom_factors_1[zoom_factor_idx_1]);
 
   // Make sure the chage was successful.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
-              zoom_factors_1[zoom_factor_idx_1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  zoom_factors_1[zoom_factor_idx_1]);
 
   // Zoom out the display. This should have no effect, since the display is
   // already at the minimum zoom level.
   display_manager()->ZoomDisplay(info_1.id(), true /* up */);
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
-              zoom_factors_1[zoom_factor_idx_1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  zoom_factors_1[zoom_factor_idx_1]);
 
   // Ensure that this call did not modify the zoom value for the other display.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(), 1.f,
-              0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  1.f);
 
   // Zoom in the display.
   display_manager()->ZoomDisplay(info_1.id(), false /* up */);
 
   // The zoom factor for the display should be set to the next zoom factor in
   // list.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
-              zoom_factors_1[zoom_factor_idx_1 + 1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  zoom_factors_1[zoom_factor_idx_1 + 1]);
 
   // Zoom out the display.
   display_manager()->ZoomDisplay(info_1.id(), true /* up */);
 
   // The zoom level should decrease from the previous level.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
-              zoom_factors_1[zoom_factor_idx_1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  zoom_factors_1[zoom_factor_idx_1]);
 
   // Enumerate the zoom factors for display.
-  const std::vector<double> zoom_factors_2 =
+  const std::vector<float> zoom_factors_2 =
       display::GetDisplayZoomFactors(modes_2[0]);
 
   // Set the zoom factor to one of the enumerated zoom factors for the said
@@ -1452,38 +1452,38 @@
                                       zoom_factors_2[zoom_factor_idx_2]);
 
   // Make sure the chage was successful.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
-              zoom_factors_2[zoom_factor_idx_2], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  zoom_factors_2[zoom_factor_idx_2]);
 
   // Zoom in the display. This should have no effect since we are already at
   // maximum zoom.
   display_manager()->ZoomDisplay(info_2.id(), false /* up */);
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
-              zoom_factors_2[zoom_factor_idx_2], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  zoom_factors_2[zoom_factor_idx_2]);
 
   // Zoom out the display
   display_manager()->ZoomDisplay(info_2.id(), true /* up */);
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
-              zoom_factors_2[zoom_factor_idx_2 - 1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  zoom_factors_2[zoom_factor_idx_2 - 1]);
 
   // Ensure that this call did not modify the zoom value for the other display.
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
-              zoom_factors_1[zoom_factor_idx_1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  zoom_factors_1[zoom_factor_idx_1]);
 
   // Reset the zoom value for displays.
   display_manager()->ResetDisplayZoom(info_1.id());
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(), 1.f,
-              0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  1.f);
   // Resetting the zoom level of one display should not effect the other display
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
-              zoom_factors_2[zoom_factor_idx_2 - 1], 0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  zoom_factors_2[zoom_factor_idx_2 - 1]);
 
   // Now reset the zoom value for other display.
   display_manager()->ResetDisplayZoom(info_2.id());
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(), 1.f,
-              0.001f);
-  EXPECT_NEAR(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(), 1.f,
-              0.001f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_2.id()).zoom_factor(),
+                  1.f);
+  EXPECT_FLOAT_EQ(display_manager()->GetDisplayInfo(info_1.id()).zoom_factor(),
+                  1.f);
 }
 
 TEST_F(DisplayManagerTest, TestDeviceScaleOnlyChange) {
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index 34c4de2..8dcd6d26 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -83,7 +83,7 @@
 constexpr char kMirroringSourceId[] = "mirroring_source_id";
 constexpr char kMirroringDestinationIds[] = "mirroring_destination_ids";
 
-constexpr char kDisplayZoom[] = "display_zoom";
+constexpr char kDisplayZoom[] = "display_zoom_factor";
 
 constexpr char kDisplayPowerAllOn[] = "all_on";
 constexpr char kDisplayPowerInternalOffExternalOn[] =
@@ -282,14 +282,12 @@
     if (ValueToInsets(*dict_value, &insets))
       insets_to_set = &insets;
 
-    // Set the default zoom percentage to 100.
-    int display_zoom_percentage = 100;
-    dict_value->GetInteger(kDisplayZoom, &display_zoom_percentage);
+    double display_zoom = 1.0;
+    dict_value->GetDouble(kDisplayZoom, &display_zoom);
 
     GetDisplayManager()->RegisterDisplayProperty(
         id, rotation, ui_scale, insets_to_set, resolution_in_pixels,
-        device_scale_factor,
-        static_cast<float>(display_zoom_percentage) / 100.f);
+        device_scale_factor, display_zoom);
   }
 }
 
@@ -577,10 +575,7 @@
                        property_value.get());
     }
 
-    // Store the display zoom as a percentage.
-    int display_zoom_percentage = std::round(info.zoom_factor() * 100.f);
-
-    property_value->SetInteger(kDisplayZoom, display_zoom_percentage);
+    property_value->SetDouble(kDisplayZoom, info.zoom_factor());
 
     pref_data->Set(base::Int64ToString(id), std::move(property_value));
   }
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index 1b124cd..b0bc64f 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -467,7 +467,7 @@
                                    1.0 /* ui_scale */,
                                    1.25f /* device_scale_factor */);
   display_manager()->SetDisplayMode(id2, mode);
-  float zoom_factor_1 = 1.75f;
+  float zoom_factor_1 = 1.f / 2.25f;
   float zoom_factor_2 = 1.60f;
   display_manager()->UpdateZoomFactor(id1, zoom_factor_1);
   display_manager()->UpdateZoomFactor(id2, zoom_factor_2);
@@ -483,9 +483,9 @@
   EXPECT_FALSE(property->GetInteger("width", &width));
   EXPECT_FALSE(property->GetInteger("height", &height));
 
-  int display_zoom_1;
-  EXPECT_TRUE(property->GetInteger("display_zoom", &display_zoom_1));
-  EXPECT_EQ(display_zoom_1, static_cast<int>(zoom_factor_1 * 100));
+  double display_zoom_1;
+  EXPECT_TRUE(property->GetDouble("display_zoom_factor", &display_zoom_1));
+  EXPECT_NEAR(display_zoom_1, zoom_factor_1, 0.0001);
 
   // External display's resolution must be stored this time because
   // it's not best.
@@ -499,9 +499,9 @@
   EXPECT_EQ(200, height);
   EXPECT_EQ(1250, device_scale_factor);
 
-  int display_zoom_2;
-  EXPECT_TRUE(property->GetInteger("display_zoom", &display_zoom_2));
-  EXPECT_EQ(display_zoom_2, static_cast<int>(zoom_factor_2 * 100));
+  double display_zoom_2;
+  EXPECT_TRUE(property->GetDouble("display_zoom_factor", &display_zoom_2));
+  EXPECT_NEAR(display_zoom_2, zoom_factor_2, 0.0001);
 
   // The layout is swapped.
   EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc b/ash/frame/custom_frame_header.cc
similarity index 72%
rename from chrome/browser/ui/views/frame/browser_frame_header_ash.cc
rename to ash/frame/custom_frame_header.cc
index 933f6846..216c342 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
+++ b/ash/frame/custom_frame_header.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/frame/browser_frame_header_ash.h"
+#include "ash/frame/custom_frame_header.h"
 
 #include "ash/ash_layout_constants.h"
 #include "ash/frame/caption_buttons/frame_caption_button.h"
@@ -25,6 +25,8 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
+namespace ash {
+
 namespace {
 
 // Color for the window title text.
@@ -100,47 +102,40 @@
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserFrameHeaderAsh, public:
+// CustomFrameHeader, public:
 
-BrowserFrameHeaderAsh::BrowserFrameHeaderAsh() = default;
+CustomFrameHeader::CustomFrameHeader() = default;
 
-BrowserFrameHeaderAsh::~BrowserFrameHeaderAsh() = default;
+CustomFrameHeader::~CustomFrameHeader() = default;
 
-void BrowserFrameHeaderAsh::Init(
+void CustomFrameHeader::Init(
     views::View* view,
     AppearanceProvider* appearance_provider,
     bool incognito,
-    views::View* window_icon,
-    ash::FrameCaptionButtonContainerView* caption_button_container,
-    ash::FrameCaptionButton* back_button) {
+    FrameCaptionButtonContainerView* caption_button_container) {
   DCHECK(view);
   DCHECK(appearance_provider);
-  // window_icon may be null.
   DCHECK(caption_button_container);
-  // back_button may be null.
 
   view_ = view;
   appearance_provider_ = appearance_provider;
   is_incognito_ = incognito;
-  window_icon_ = window_icon;
   caption_button_container_ = caption_button_container;
-  back_button_ = back_button;
 }
 
-int BrowserFrameHeaderAsh::GetMinimumHeaderWidth() const {
+int CustomFrameHeader::GetMinimumHeaderWidth() const {
   // Ensure we have enough space for the window icon and buttons. We allow
   // the title string to collapse to zero width.
   return GetTitleBounds().x() +
          caption_button_container_->GetMinimumSize().width();
 }
 
-void BrowserFrameHeaderAsh::PaintHeader(gfx::Canvas* canvas, Mode mode) {
+void CustomFrameHeader::PaintHeader(gfx::Canvas* canvas, Mode mode) {
   Mode old_mode = mode_;
   mode_ = mode;
 
   if (mode_ != old_mode) {
-    if (!initial_paint_ &&
-        ash::FrameHeaderUtil::CanAnimateActivation(GetWidget())) {
+    if (!initial_paint_ && FrameHeaderUtil::CanAnimateActivation(GetWidget())) {
       activation_animation_.SetSlideDuration(kActivationCrossfadeDurationMs);
       if (mode_ == MODE_ACTIVE)
         activation_animation_.Show();
@@ -164,31 +159,30 @@
   }
 }
 
-void BrowserFrameHeaderAsh::LayoutHeader() {
-  // Purposefully set |painted_height_| to an invalid value. We cannot use
-  // |painted_height_| because the computation of |painted_height_| may depend
-  // on having laid out the window controls.
-  painted_height_ = -1;
+void CustomFrameHeader::LayoutHeader() {
   LayoutHeaderInternal();
+  // Default to the header height; owning code may override via
+  // SetHeaderHeightForPainting().
+  painted_height_ = GetHeaderHeight();
 }
 
-int BrowserFrameHeaderAsh::GetHeaderHeight() const {
+int CustomFrameHeader::GetHeaderHeight() const {
   return caption_button_container_->height();
 }
 
-int BrowserFrameHeaderAsh::GetHeaderHeightForPainting() const {
+int CustomFrameHeader::GetHeaderHeightForPainting() const {
   return painted_height_;
 }
 
-void BrowserFrameHeaderAsh::SetHeaderHeightForPainting(int height) {
+void CustomFrameHeader::SetHeaderHeightForPainting(int height) {
   painted_height_ = height;
 }
 
-void BrowserFrameHeaderAsh::SchedulePaintForTitle() {
+void CustomFrameHeader::SchedulePaintForTitle() {
   view_->SchedulePaintInRect(GetTitleBounds());
 }
 
-void BrowserFrameHeaderAsh::SetPaintAsActive(bool paint_as_active) {
+void CustomFrameHeader::SetPaintAsActive(bool paint_as_active) {
   SkColor frame_color =
       appearance_provider_->GetFrameHeaderColor(paint_as_active);
   caption_button_container_->SetPaintAsActive(paint_as_active);
@@ -199,7 +193,7 @@
   }
 }
 
-void BrowserFrameHeaderAsh::OnShowStateChanged(ui::WindowShowState show_state) {
+void CustomFrameHeader::OnShowStateChanged(ui::WindowShowState show_state) {
   if (show_state == ui::SHOW_STATE_MINIMIZED)
     return;
   // Call LayoutHeaderInternal() instead of LayoutHeader() here because
@@ -207,18 +201,34 @@
   LayoutHeaderInternal();
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// gfx::AnimationDelegate overrides:
+void CustomFrameHeader::SetLeftHeaderView(views::View* left_header_view) {
+  window_icon_ = left_header_view;
+}
 
-void BrowserFrameHeaderAsh::AnimationProgressed(
-    const gfx::Animation* animation) {
+void CustomFrameHeader::SetBackButton(FrameCaptionButton* back_button) {
+  back_button_ = back_button;
+}
+
+FrameCaptionButton* CustomFrameHeader::GetBackButton() const {
+  return back_button_;
+}
+
+void CustomFrameHeader::SetFrameColors(SkColor active_frame_color,
+                                       SkColor inactive_frame_color) {
   view_->SchedulePaintInRect(GetPaintedBounds());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserFrameHeaderAsh, private:
+// gfx::AnimationDelegate overrides:
 
-void BrowserFrameHeaderAsh::LayoutHeaderInternal() {
+void CustomFrameHeader::AnimationProgressed(const gfx::Animation* animation) {
+  view_->SchedulePaintInRect(GetPaintedBounds());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameHeader, private:
+
+void CustomFrameHeader::LayoutHeaderInternal() {
   UpdateCaptionButtons();
   const gfx::Size caption_button_container_size =
       caption_button_container_->GetPreferredSize();
@@ -236,11 +246,11 @@
   // container.
   const gfx::Size icon_size(window_icon_->GetPreferredSize());
   const int icon_offset_y = (GetHeaderHeight() - icon_size.height()) / 2;
-  window_icon_->SetBounds(ash::FrameHeaderUtil::GetLeftViewXInset(),
-                          icon_offset_y, icon_size.width(), icon_size.height());
+  window_icon_->SetBounds(FrameHeaderUtil::GetLeftViewXInset(), icon_offset_y,
+                          icon_size.width(), icon_size.height());
 }
 
-void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas, bool active) {
+void CustomFrameHeader::PaintFrameImages(gfx::Canvas* canvas, bool active) {
   int alpha = activation_animation_.CurrentValueBetween(0, 0xFF);
   if (!active)
     alpha = 0xFF - alpha;
@@ -257,15 +267,15 @@
 
   int corner_radius = 0;
   if (!GetWidget()->IsMaximized() && !GetWidget()->IsFullscreen())
-    corner_radius = ash::FrameHeaderUtil::GetTopCornerRadiusWhenRestored();
+    corner_radius = FrameHeaderUtil::GetTopCornerRadiusWhenRestored();
 
   PaintFrameImagesInRoundRect(canvas, frame_image, frame_overlay_image, alpha,
                               background_color, GetPaintedBounds(),
                               corner_radius,
-                              ash::FrameHeaderUtil::GetThemeBackgroundXInset());
+                              FrameHeaderUtil::GetThemeBackgroundXInset());
 }
 
-void BrowserFrameHeaderAsh::PaintTitleBar(gfx::Canvas* canvas) {
+void CustomFrameHeader::PaintTitleBar(gfx::Canvas* canvas) {
   // The window icon is painted by its own views::View.
   canvas->DrawStringRectWithFlags(
       GetWidget()->widget_delegate()->GetWindowTitle(),
@@ -276,21 +286,19 @@
       gfx::Canvas::NO_SUBPIXEL_RENDERING);
 }
 
-void BrowserFrameHeaderAsh::UpdateCaptionButtons() {
+void CustomFrameHeader::UpdateCaptionButtons() {
   // When |frame_| minimized, avoid tablet mode toggling to update caption
   // buttons as it would cause mismatch beteen window state and size button.
   if (GetWidget()->IsMinimized())
     return;
-  caption_button_container_->SetButtonImage(ash::CAPTION_BUTTON_ICON_MINIMIZE,
-                                            ash::kWindowControlMinimizeIcon);
-  caption_button_container_->SetButtonImage(ash::CAPTION_BUTTON_ICON_CLOSE,
-                                            ash::kWindowControlCloseIcon);
-  caption_button_container_->SetButtonImage(
-      ash::CAPTION_BUTTON_ICON_LEFT_SNAPPED,
-      ash::kWindowControlLeftSnappedIcon);
-  caption_button_container_->SetButtonImage(
-      ash::CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
-      ash::kWindowControlRightSnappedIcon);
+  caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_MINIMIZE,
+                                            kWindowControlMinimizeIcon);
+  caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_CLOSE,
+                                            kWindowControlCloseIcon);
+  caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+                                            kWindowControlLeftSnappedIcon);
+  caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+                                            kWindowControlRightSnappedIcon);
 
   const bool is_maximized_or_fullscreen =
       GetWidget()->IsMaximized() || GetWidget()->IsFullscreen();
@@ -298,25 +306,27 @@
       is_maximized_or_fullscreen || appearance_provider_->IsTabletMode()
           ? AshLayoutSize::kBrowserCaptionMaximized
           : AshLayoutSize::kBrowserCaptionRestored;
-  const gfx::VectorIcon* const size_icon =
-      is_maximized_or_fullscreen ? &ash::kWindowControlRestoreIcon
-                                 : &ash::kWindowControlMaximizeIcon;
+  const gfx::VectorIcon* const size_icon = is_maximized_or_fullscreen
+                                               ? &kWindowControlRestoreIcon
+                                               : &kWindowControlMaximizeIcon;
 
   caption_button_container_->SetButtonImage(
-      ash::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, *size_icon);
+      CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, *size_icon);
   caption_button_container_->SetButtonSize(GetAshLayoutSize(button_size_type));
 }
 
-gfx::Rect BrowserFrameHeaderAsh::GetPaintedBounds() const {
+gfx::Rect CustomFrameHeader::GetPaintedBounds() const {
   return gfx::Rect(view_->width(), painted_height_);
 }
 
-gfx::Rect BrowserFrameHeaderAsh::GetTitleBounds() const {
+gfx::Rect CustomFrameHeader::GetTitleBounds() const {
   views::View* left_view = window_icon_ ? window_icon_ : back_button_;
-  return ash::FrameHeaderUtil::GetAvailableTitleBounds(
+  return FrameHeaderUtil::GetAvailableTitleBounds(
       left_view, caption_button_container_, GetHeaderHeight());
 }
 
-views::Widget* BrowserFrameHeaderAsh::GetWidget() {
+views::Widget* CustomFrameHeader::GetWidget() {
   return view_->GetWidget();
 }
+
+}  // namespace ash
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.h b/ash/frame/custom_frame_header.h
similarity index 74%
rename from chrome/browser/ui/views/frame/browser_frame_header_ash.h
rename to ash/frame/custom_frame_header.h
index 0a7db2d..8eaa05b1 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.h
+++ b/ash/frame/custom_frame_header.h
@@ -2,43 +2,47 @@
 // 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_FRAME_BROWSER_FRAME_HEADER_ASH_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_HEADER_ASH_H_
+#ifndef ASH_FRAME_CUSTOM_FRAME_HEADER_H_
+#define ASH_FRAME_CUSTOM_FRAME_HEADER_H_
 
+#include "ash/ash_export.h"
 #include "ash/frame/frame_header.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/slide_animation.h"
 
-namespace ash {
-class FrameCaptionButton;
-class FrameCaptionButtonContainerView;
-}  // namespace ash
-
 namespace gfx {
 class ImageSkia;
 class Rect;
 }  // namespace gfx
+
 namespace views {
 class View;
 class Widget;
 }  // namespace views
 
-// Helper class for managing the browser window header.
-class BrowserFrameHeaderAsh : public ash::FrameHeader,
-                              public gfx::AnimationDelegate {
+namespace ash {
+
+class FrameCaptionButton;
+class FrameCaptionButtonContainerView;
+
+// Helper class for drawing a custom frame (such as for a themed Chrome Browser
+// frame).
+class ASH_EXPORT CustomFrameHeader : public FrameHeader,
+                                     public gfx::AnimationDelegate {
  public:
   class AppearanceProvider {
    public:
+    virtual ~AppearanceProvider() = default;
     virtual SkColor GetFrameHeaderColor(bool active) = 0;
     virtual gfx::ImageSkia GetFrameHeaderImage(bool active) = 0;
     virtual gfx::ImageSkia GetFrameHeaderOverlayImage(bool active) = 0;
     virtual bool IsTabletMode() = 0;
   };
 
-  BrowserFrameHeaderAsh();
-  ~BrowserFrameHeaderAsh() override;
+  CustomFrameHeader();
+  ~CustomFrameHeader() override;
 
   // BrowserFrameHeaderAsh does not take ownership of any of the parameters.
   // |view| is the view into which |this| will paint. |back_button| can be
@@ -46,11 +50,9 @@
   void Init(views::View* view,
             AppearanceProvider* appearance_provider,
             bool incognito,
-            views::View* window_icon,
-            ash::FrameCaptionButtonContainerView* caption_button_container,
-            ash::FrameCaptionButton* back_button);
+            FrameCaptionButtonContainerView* caption_button_container);
 
-  // ash::FrameHeader overrides:
+  // FrameHeader overrides:
   int GetMinimumHeaderWidth() const override;
   void PaintHeader(gfx::Canvas* canvas, Mode mode) override;
   void LayoutHeader() override;
@@ -60,6 +62,11 @@
   void SchedulePaintForTitle() override;
   void SetPaintAsActive(bool paint_as_active) override;
   void OnShowStateChanged(ui::WindowShowState show_state) override;
+  void SetLeftHeaderView(views::View* left_header_view) override;
+  void SetBackButton(FrameCaptionButton* back_button) override;
+  FrameCaptionButton* GetBackButton() const override;
+  void SetFrameColors(SkColor active_frame_color,
+                      SkColor inactive_frame_color) override;
 
  private:
   // gfx::AnimationDelegate override:
@@ -98,8 +105,8 @@
   bool is_incognito_ = false;
 
   views::View* window_icon_ = nullptr;
-  ash::FrameCaptionButton* back_button_ = nullptr;
-  ash::FrameCaptionButtonContainerView* caption_button_container_ = nullptr;
+  FrameCaptionButton* back_button_ = nullptr;
+  FrameCaptionButtonContainerView* caption_button_container_ = nullptr;
   int painted_height_ = 0;
 
   // Whether the header is painted for the first time.
@@ -110,7 +117,9 @@
 
   gfx::SlideAnimation activation_animation_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(BrowserFrameHeaderAsh);
+  DISALLOW_COPY_AND_ASSIGN(CustomFrameHeader);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_HEADER_ASH_H_
+}  // namespace ash
+
+#endif  // ASH_FRAME_CUSTOM_FRAME_HEADER_H_
diff --git a/ash/frame/custom_frame_view_ash.cc b/ash/frame/custom_frame_view_ash.cc
index dc1a0e5b..27249f34 100644
--- a/ash/frame/custom_frame_view_ash.cc
+++ b/ash/frame/custom_frame_view_ash.cc
@@ -10,6 +10,7 @@
 
 #include "ash/frame/caption_buttons/frame_caption_button.h"
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "ash/frame/default_frame_header.h"
 #include "ash/frame/frame_border_hit_test.h"
 #include "ash/frame/header_view.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
@@ -179,48 +180,6 @@
 bool CustomFrameViewAsh::use_empty_minimum_size_for_test_ = false;
 
 ///////////////////////////////////////////////////////////////////////////////
-// CustomFrameViewAsh::AvatarObserver
-
-// AvatarObserver watches the frame window's avatar icon property and updates
-// HeaderView with it.
-class CustomFrameViewAsh::AvatarObserver : public aura::WindowObserver {
- public:
-  AvatarObserver(views::Widget* frame, HeaderView* header_view)
-      : frame_window_(frame->GetNativeWindow()), header_view_(header_view) {
-    frame_window_->AddObserver(this);
-  }
-
-  ~AvatarObserver() override {
-    if (frame_window_)
-      frame_window_->RemoveObserver(this);
-  }
-
-  // aura::WindowObserver:
-  void OnWindowPropertyChanged(aura::Window* window,
-                               const void* key,
-                               intptr_t old) override {
-    DCHECK_EQ(frame_window_, window);
-    if (key != aura::client::kAvatarIconKey)
-      return;
-
-    gfx::ImageSkia* const avatar_icon =
-        frame_window_->GetProperty(aura::client::kAvatarIconKey);
-    header_view_->SetAvatarIcon(avatar_icon ? *avatar_icon : gfx::ImageSkia());
-  }
-
-  void OnWindowDestroyed(aura::Window* window) override {
-    DCHECK_EQ(frame_window_, window);
-    frame_window_ = nullptr;
-  }
-
- private:
-  aura::Window* frame_window_;
-  HeaderView* const header_view_;
-
-  DISALLOW_COPY_AND_ASSIGN(AvatarObserver);
-};
-
-///////////////////////////////////////////////////////////////////////////////
 // CustomFrameViewAsh::OverlayView
 
 // View which takes up the entire widget and contains the HeaderView. HeaderView
@@ -317,19 +276,18 @@
       header_view_(new HeaderView(frame, window_style, std::move(model))),
       overlay_view_(new OverlayView(header_view_)),
       immersive_delegate_(immersive_delegate ? immersive_delegate
-                                             : header_view_),
-      avatar_observer_(std::make_unique<AvatarObserver>(frame_, header_view_)) {
+                                             : header_view_) {
   aura::Window* frame_window = frame->GetNativeWindow();
   wm::InstallResizeHandleWindowTargeterForWindow(frame_window, nullptr);
   // |header_view_| is set as the non client view's overlay view so that it can
   // overlay the web contents in immersive fullscreen.
   frame->non_client_view()->SetOverlayView(overlay_view_);
   frame_window->SetProperty(aura::client::kTopViewColor,
-                            header_view_->GetInactiveFrameColor());
+                            DefaultFrameHeader::GetDefaultFrameColor());
   frame_window->SetProperty(ash::kFrameActiveColorKey,
-                            header_view_->GetActiveFrameColor());
+                            DefaultFrameHeader::GetDefaultFrameColor());
   frame_window->SetProperty(ash::kFrameInactiveColorKey,
-                            header_view_->GetInactiveFrameColor());
+                            DefaultFrameHeader::GetDefaultFrameColor());
   frame_window->AddObserver(this);
 
   // A delegate for a more complex way of fullscreening the window may already
@@ -362,7 +320,6 @@
 
 void CustomFrameViewAsh::SetFrameColors(SkColor active_frame_color,
                                         SkColor inactive_frame_color) {
-  header_view_->SetFrameColors(active_frame_color, inactive_frame_color);
   aura::Window* frame_window = frame_->GetNativeWindow();
   frame_window->SetProperty(aura::client::kTopViewColor, inactive_frame_color);
   frame_window->SetProperty(ash::kFrameActiveColorKey, active_frame_color);
@@ -428,7 +385,6 @@
 void CustomFrameViewAsh::UpdateWindowIcon() {}
 
 void CustomFrameViewAsh::UpdateWindowTitle() {
-  header_view_->set_title(GetFrameTitle());
   header_view_->SchedulePaintForTitle();
 }
 
@@ -527,19 +483,6 @@
   if (key == aura::client::kShowStateKey) {
     header_view_->OnShowStateChanged(
         window->GetProperty(aura::client::kShowStateKey));
-    return;
-  }
-
-  if (key == ash::kFrameActiveColorKey) {
-    header_view_->SetFrameColors(window->GetProperty(ash::kFrameActiveColorKey),
-                                 header_view_->GetInactiveFrameColor());
-    return;
-  }
-
-  if (key == ash::kFrameInactiveColorKey) {
-    header_view_->SetFrameColors(
-        header_view_->GetActiveFrameColor(),
-        window->GetProperty(ash::kFrameInactiveColorKey));
   }
 }
 
@@ -548,11 +491,11 @@
 }
 
 SkColor CustomFrameViewAsh::GetActiveFrameColorForTest() const {
-  return header_view_->GetActiveFrameColor();
+  return frame_->GetNativeWindow()->GetProperty(ash::kFrameActiveColorKey);
 }
 
 SkColor CustomFrameViewAsh::GetInactiveFrameColorForTest() const {
-  return header_view_->GetInactiveFrameColor();
+  return frame_->GetNativeWindow()->GetProperty(ash::kFrameInactiveColorKey);
 }
 
 void CustomFrameViewAsh::UpdateHeaderView() {
diff --git a/ash/frame/custom_frame_view_ash.h b/ash/frame/custom_frame_view_ash.h
index a7af5ef..3c2d7ee 100644
--- a/ash/frame/custom_frame_view_ash.h
+++ b/ash/frame/custom_frame_view_ash.h
@@ -144,7 +144,6 @@
   void UpdateHeaderView();
 
  private:
-  class AvatarObserver;
   class OverlayView;
   friend class CustomFrameViewAshSizeLock;
   friend class CustomFrameTestWidgetDelegate;
@@ -171,9 +170,6 @@
 
   ImmersiveFullscreenControllerDelegate* immersive_delegate_;
 
-  // Observes avatar icon change and updates |header_view_|.
-  std::unique_ptr<AvatarObserver> avatar_observer_;
-
   static bool use_empty_minimum_size_for_test_;
 
   // Track whether the device is in overview mode. Set this to true when
diff --git a/ash/frame/default_frame_header.cc b/ash/frame/default_frame_header.cc
index 3e8de22..e18a32b 100644
--- a/ash/frame/default_frame_header.cc
+++ b/ash/frame/default_frame_header.cc
@@ -10,14 +10,12 @@
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/frame/frame_header_util.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
-#include "ash/resources/grit/ash_resources.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/debug/leak_annotations.h"
 #include "base/logging.h"  // DCHECK
 #include "third_party/skia/include/core/SkPath.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
@@ -38,8 +36,6 @@
 // Color for the window title text.
 const SkColor kTitleTextColor = SkColorSetRGB(40, 40, 40);
 const SkColor kLightTitleTextColor = SK_ColorWHITE;
-// The default color of the frame.
-const SkColor kDefaultFrameColor = SkColorSetRGB(0xFD, 0xFE, 0xFF);
 // Duration of crossfade animation for activating and deactivating frame.
 const int kActivationCrossfadeDurationMs = 200;
 
@@ -73,15 +69,13 @@
 DefaultFrameHeader::DefaultFrameHeader(
     views::Widget* frame,
     views::View* header_view,
-    FrameCaptionButtonContainerView* caption_button_container,
-    mojom::WindowStyle window_style)
-    : window_style_(window_style),
-      frame_(frame),
+    FrameCaptionButtonContainerView* caption_button_container)
+    : frame_(frame),
       view_(header_view),
       back_button_(nullptr),
       left_header_view_(nullptr),
-      active_frame_color_(kDefaultFrameColor),
-      inactive_frame_color_(kDefaultFrameColor),
+      active_frame_color_(GetDefaultFrameColor()),
+      inactive_frame_color_(GetDefaultFrameColor()),
       caption_button_container_(caption_button_container),
       painted_height_(0),
       mode_(MODE_INACTIVE),
@@ -136,28 +130,11 @@
   flags.setAntiAlias(true);
   TileRoundRect(canvas, flags, GetLocalBounds(), corner_radius);
 
-  if (!frame_->IsMaximized() && !frame_->IsFullscreen() &&
-      mode_ == MODE_INACTIVE && !UsesCustomFrameColors()) {
-    PaintHighlightForInactiveRestoredWindow(canvas);
-  }
-  if (frame_->widget_delegate()->ShouldShowWindowTitle() && !title_.empty())
+  if (frame_->widget_delegate()->ShouldShowWindowTitle() && !GetTitle().empty())
     PaintTitleBar(canvas);
 }
 
 void DefaultFrameHeader::LayoutHeader() {
-  // TODO(sky): this needs to reset images as well.
-  if (window_style_ == mojom::WindowStyle::BROWSER) {
-    const bool is_in_tablet_mode = Shell::Get()
-                                       ->tablet_mode_controller()
-                                       ->IsTabletModeWindowManagerEnabled();
-    const bool use_maximized_size =
-        frame_->IsMaximized() || frame_->IsFullscreen() || is_in_tablet_mode;
-    const gfx::Size button_size(GetAshLayoutSize(
-        use_maximized_size ? AshLayoutSize::kBrowserCaptionMaximized
-                           : AshLayoutSize::kBrowserCaptionRestored));
-    caption_button_container_->SetButtonSize(button_size);
-  }
-
   caption_button_container_->SetBackgroundColor(GetCurrentFrameColor());
   caption_button_container_->SetColorMode(button_color_mode_);
   UpdateSizeButtonImages();
@@ -222,6 +199,18 @@
   LayoutHeader();
 }
 
+void DefaultFrameHeader::SetLeftHeaderView(views::View* left_header_view) {
+  left_header_view_ = left_header_view;
+}
+
+void DefaultFrameHeader::SetBackButton(FrameCaptionButton* back_button) {
+  back_button_ = back_button;
+}
+
+FrameCaptionButton* DefaultFrameHeader::GetBackButton() const {
+  return back_button_;
+}
+
 void DefaultFrameHeader::SetFrameColors(SkColor active_frame_color,
                                         SkColor inactive_frame_color) {
   button_color_mode_ = FrameCaptionButton::ColorMode::kDefault;
@@ -233,10 +222,6 @@
   SetFrameColorsImpl(theme_color, theme_color);
 }
 
-SkColor DefaultFrameHeader::GetCurrentFrameColor() const {
-  return mode_ == MODE_ACTIVE ? active_frame_color_ : inactive_frame_color_;
-}
-
 void DefaultFrameHeader::SetFrameColorsImpl(SkColor active_frame_color,
                                             SkColor inactive_frame_color) {
   bool updated = false;
@@ -255,19 +240,16 @@
   }
 }
 
-SkColor DefaultFrameHeader::GetActiveFrameColor() const {
-  return active_frame_color_;
-}
-
-SkColor DefaultFrameHeader::GetInactiveFrameColor() const {
-  return inactive_frame_color_;
-}
-
 SkColor DefaultFrameHeader::GetTitleColor() const {
   return color_utils::IsDark(GetCurrentFrameColor()) ? kLightTitleTextColor
                                                      : kTitleTextColor;
 }
 
+// static
+SkColor DefaultFrameHeader::GetDefaultFrameColor() {
+  return SkColorSetRGB(0xFD, 0xFE, 0xFF);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // gfx::AnimationDelegate overrides:
 
@@ -278,39 +260,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 // DefaultFrameHeader, private:
 
-void DefaultFrameHeader::PaintHighlightForInactiveRestoredWindow(
-    gfx::Canvas* canvas) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  gfx::ImageSkia top_edge =
-      *rb.GetImageSkiaNamed(IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP);
-  gfx::ImageSkia left_edge =
-      *rb.GetImageSkiaNamed(IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT);
-  gfx::ImageSkia right_edge =
-      *rb.GetImageSkiaNamed(IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT);
-  gfx::ImageSkia bottom_edge =
-      *rb.GetImageSkiaNamed(IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM);
-
-  int left_edge_width = left_edge.width();
-  int right_edge_width = right_edge.width();
-  canvas->DrawImageInt(left_edge, 0, 0);
-  canvas->DrawImageInt(right_edge, view_->width() - right_edge_width, 0);
-  canvas->TileImageInt(top_edge, left_edge_width, 0,
-                       view_->width() - left_edge_width - right_edge_width,
-                       top_edge.height());
-
-  DCHECK_EQ(left_edge.height(), right_edge.height());
-  int bottom = left_edge.height();
-  int bottom_height = bottom_edge.height();
-  canvas->TileImageInt(bottom_edge, left_edge_width, bottom - bottom_height,
-                       view_->width() - left_edge_width - right_edge_width,
-                       bottom_height);
-}
-
 void DefaultFrameHeader::PaintTitleBar(gfx::Canvas* canvas) {
   // The window icon is painted by its own views::View.
   gfx::Rect title_bounds = GetAvailableTitleBounds();
   title_bounds.set_x(view_->GetMirroredXForRect(title_bounds));
-  canvas->DrawStringRect(title_,
+  canvas->DrawStringRect(GetTitle(),
                          views::NativeWidgetAura::GetWindowTitleFontList(),
                          GetTitleColor(), title_bounds);
 }
@@ -371,9 +325,12 @@
       left_view, caption_button_container_, GetHeaderHeight());
 }
 
-bool DefaultFrameHeader::UsesCustomFrameColors() const {
-  return active_frame_color_ != kDefaultFrameColor ||
-         inactive_frame_color_ != kDefaultFrameColor;
+base::string16 DefaultFrameHeader::GetTitle() const {
+  return frame_->widget_delegate()->GetWindowTitle();
+}
+
+SkColor DefaultFrameHeader::GetCurrentFrameColor() const {
+  return mode_ == MODE_ACTIVE ? active_frame_color_ : inactive_frame_color_;
 }
 
 }  // namespace ash
diff --git a/ash/frame/default_frame_header.h b/ash/frame/default_frame_header.h
index 7e541ce..59fcc12 100644
--- a/ash/frame/default_frame_header.h
+++ b/ash/frame/default_frame_header.h
@@ -10,7 +10,6 @@
 #include "ash/ash_export.h"
 #include "ash/frame/caption_buttons/frame_caption_button.h"
 #include "ash/frame/frame_header.h"
-#include "ash/public/interfaces/window_style.mojom.h"
 #include "base/compiler_specific.h"  // override
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -37,11 +36,9 @@
                                       public gfx::AnimationDelegate {
  public:
   // DefaultFrameHeader does not take ownership of any of the parameters.
-  DefaultFrameHeader(
-      views::Widget* frame,
-      views::View* header_view,
-      FrameCaptionButtonContainerView* caption_button_container,
-      mojom::WindowStyle window_style = mojom::WindowStyle::DEFAULT);
+  DefaultFrameHeader(views::Widget* frame,
+                     views::View* header_view,
+                     FrameCaptionButtonContainerView* caption_button_container);
   ~DefaultFrameHeader() override;
 
   // FrameHeader overrides:
@@ -54,29 +51,23 @@
   void SchedulePaintForTitle() override;
   void SetPaintAsActive(bool paint_as_active) override;
   void OnShowStateChanged(ui::WindowShowState show_state) override;
+  void SetLeftHeaderView(views::View* left_header_view) override;
+  void SetBackButton(FrameCaptionButton* back_button) override;
+  FrameCaptionButton* GetBackButton() const override;
+  void SetFrameColors(SkColor active_frame_color,
+                      SkColor inactive_frame_color) override;
 
-  void set_left_header_view(views::View* left_header_view) {
-    left_header_view_ = left_header_view;
-  }
-
-  void set_back_button(FrameCaptionButton* back_button) {
-    back_button_ = back_button;
-  }
-  FrameCaptionButton* back_button() { return back_button_; }
-
-  void set_title(const base::string16& title) { title_ = title; }
-
-  // Sets the active and inactive frame colors. Note the inactive frame color
-  // will have some transparency added when the frame is drawn.
-  void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color);
   void SetThemeColor(SkColor theme_color);
-  SkColor GetActiveFrameColor() const;
-  SkColor GetInactiveFrameColor() const;
-  SkColor GetCurrentFrameColor() const;
 
   // Gets the color of the title text.
   SkColor GetTitleColor() const;
 
+  // The default frame color for both active and inactive.
+  static SkColor GetDefaultFrameColor();
+
+  SkColor active_frame_color_for_testing() { return active_frame_color_; }
+  SkColor inactive_frame_color_for_testing() { return inactive_frame_color_; }
+
  protected:
   // Updates the frame colors and ensures buttons are up to date.
   void SetFrameColorsImpl(SkColor active_frame_color,
@@ -98,10 +89,6 @@
   // gfx::AnimationDelegate override:
   void AnimationProgressed(const gfx::Animation* animation) override;
 
-  // Paints highlight around the edge of the header for inactive restored
-  // windows.
-  void PaintHighlightForInactiveRestoredWindow(gfx::Canvas* canvas);
-
   // Update all the images in the caption buttons.
   void UpdateAllButtonImages();
 
@@ -113,10 +100,10 @@
   // same width as |view_|.
   gfx::Rect GetLocalBounds() const;
 
-  // Returns whether the frame uses custom frame coloring.
-  bool UsesCustomFrameColors() const;
+  base::string16 GetTitle() const;
 
-  const mojom::WindowStyle window_style_;
+  SkColor GetCurrentFrameColor() const;
+
   views::Widget* frame_;
   views::View* view_;
   FrameCaptionButton* back_button_;  // May be nullptr.
@@ -126,7 +113,6 @@
   SkColor active_frame_color_;
   SkColor inactive_frame_color_;
   FrameCaptionButtonContainerView* caption_button_container_;
-  base::string16 title_;
 
   // The height of the header to paint.
   int painted_height_;
diff --git a/ash/frame/default_frame_header_unittest.cc b/ash/frame/default_frame_header_unittest.cc
index 4077dc1..90ac002d 100644
--- a/ash/frame/default_frame_header_unittest.cc
+++ b/ash/frame/default_frame_header_unittest.cc
@@ -33,7 +33,7 @@
 
   DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
                                   &container);
-  frame_header.set_left_header_view(&window_icon);
+  frame_header.SetLeftHeaderView(&window_icon);
   frame_header.LayoutHeader();
   gfx::Rect title_bounds = frame_header.GetAvailableTitleBounds();
   EXPECT_EQ(window_icon.bounds().CenterPoint().y(),
@@ -48,7 +48,7 @@
 
   DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
                                   &container);
-  frame_header.set_back_button(&back);
+  frame_header.SetBackButton(&back);
   frame_header.LayoutHeader();
   gfx::Rect title_bounds = frame_header.GetAvailableTitleBounds();
   // The back button should be positioned at the left edge, and
diff --git a/ash/frame/frame_header.h b/ash/frame/frame_header.h
index 9d799664..0878a5a 100644
--- a/ash/frame/frame_header.h
+++ b/ash/frame/frame_header.h
@@ -6,14 +6,21 @@
 #define ASH_FRAME_FRAME_HEADER_H_
 
 #include "ash/ash_export.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/ui_base_types.h"
 
 namespace gfx {
 class Canvas;
 }
 
+namespace views {
+class View;
+}
+
 namespace ash {
 
+class FrameCaptionButton;
+
 // Helper class for managing the window header.
 class ASH_EXPORT FrameHeader {
  public:
@@ -48,6 +55,15 @@
 
   // Called when frame show state is changed.
   virtual void OnShowStateChanged(ui::WindowShowState show_state) = 0;
+
+  virtual void SetLeftHeaderView(views::View* view) = 0;
+  virtual void SetBackButton(FrameCaptionButton* view) = 0;
+  virtual FrameCaptionButton* GetBackButton() const = 0;
+
+  // Sets the active and inactive frame colors. Note the inactive frame color
+  // will have some transparency added when the frame is drawn.
+  virtual void SetFrameColors(SkColor active_frame_color,
+                              SkColor inactive_frame_color) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc
index d318f21..fd2e094 100644
--- a/ash/frame/header_view.cc
+++ b/ash/frame/header_view.cc
@@ -11,14 +11,59 @@
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/frame/custom_frame_view_ash.h"
 #include "ash/frame/default_frame_header.h"
+#include "ash/public/cpp/config.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
 
+namespace {
+
+// An appearance provider that relies on window properties which have been set
+// by the client. Only used in Mash.
+class WindowPropertyAppearanceProvider
+    : public CustomFrameHeader::AppearanceProvider {
+ public:
+  explicit WindowPropertyAppearanceProvider(aura::Window* window)
+      : window_(window) {}
+  ~WindowPropertyAppearanceProvider() override = default;
+
+  SkColor GetFrameHeaderColor(bool active) override {
+    return window_->GetProperty(active ? ash::kFrameActiveColorKey
+                                       : ash::kFrameInactiveColorKey);
+  }
+
+  gfx::ImageSkia GetFrameHeaderImage(bool active) override {
+    // TODO(estade): handle !active.
+    gfx::ImageSkia* image = window_->GetProperty(kFrameImageActiveKey);
+    return image ? *image : gfx::ImageSkia();
+  }
+
+  gfx::ImageSkia GetFrameHeaderOverlayImage(bool active) override {
+    // TODO(estade): implement.
+    return gfx::ImageSkia();
+  }
+
+  bool IsTabletMode() override {
+    return Shell::Get()
+        ->tablet_mode_controller()
+        ->IsTabletModeWindowManagerEnabled();
+  }
+
+ private:
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowPropertyAppearanceProvider);
+};
+
+}  // namespace
+
 HeaderView::HeaderView(views::Widget* target_widget,
                        mojom::WindowStyle window_style,
                        std::unique_ptr<CaptionButtonModel> model)
@@ -32,9 +77,22 @@
   caption_button_container_->UpdateCaptionButtonState(false /*=animate*/);
   AddChildView(caption_button_container_);
 
-  frame_header_ = std::make_unique<DefaultFrameHeader>(
-      target_widget_, this, caption_button_container_, window_style);
+  if (window_style == mojom::WindowStyle::DEFAULT) {
+    frame_header_ = std::make_unique<DefaultFrameHeader>(
+        target_widget_, this, caption_button_container_);
+  } else {
+    DCHECK_EQ(mojom::WindowStyle::BROWSER, window_style);
+    DCHECK_EQ(Config::MASH, Shell::GetAshConfig());
+    appearance_provider_ = std::make_unique<WindowPropertyAppearanceProvider>(
+        target_widget_->GetNativeWindow());
+    auto frame_header = std::make_unique<CustomFrameHeader>();
+    // TODO(estade): pass correct value for |incognito|.
+    frame_header->Init(this, appearance_provider_.get(), false,
+                       caption_button_container_);
+    frame_header_ = std::move(frame_header);
+  }
 
+  window_observer_.Add(target_widget_->GetNativeWindow());
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
 
@@ -85,7 +143,7 @@
     }
     avatar_icon_->SetImage(avatar);
   }
-  frame_header_->set_left_header_view(avatar_icon_);
+  frame_header_->SetLeftHeaderView(avatar_icon_);
   Layout();
 }
 
@@ -95,36 +153,23 @@
 
   bool has_back_button =
       caption_button_container_->model()->IsVisible(CAPTION_BUTTON_ICON_BACK);
-  FrameCaptionButton* back_button = frame_header_->back_button();
+  FrameCaptionButton* back_button = frame_header_->GetBackButton();
   if (has_back_button) {
     if (!back_button) {
       back_button = new FrameBackButton();
       AddChildView(back_button);
-      frame_header_->set_back_button(back_button);
+      frame_header_->SetBackButton(back_button);
     }
     back_button->SetEnabled(caption_button_container_->model()->IsEnabled(
         CAPTION_BUTTON_ICON_BACK));
   } else {
     delete back_button;
-    frame_header_->set_back_button(nullptr);
+    frame_header_->SetBackButton(nullptr);
   }
 
   Layout();
 }
 
-void HeaderView::SetFrameColors(SkColor active_frame_color,
-                                SkColor inactive_frame_color) {
-  frame_header_->SetFrameColors(active_frame_color, inactive_frame_color);
-}
-
-SkColor HeaderView::GetActiveFrameColor() const {
-  return frame_header_->GetActiveFrameColor();
-}
-
-SkColor HeaderView::GetInactiveFrameColor() const {
-  return frame_header_->GetInactiveFrameColor();
-}
-
 void HeaderView::OnShowStateChanged(ui::WindowShowState show_state) {
   frame_header_->OnShowStateChanged(show_state);
 }
@@ -174,6 +219,28 @@
   target_widget_->non_client_view()->Layout();
 }
 
+void HeaderView::OnWindowPropertyChanged(aura::Window* window,
+                                         const void* key,
+                                         intptr_t old) {
+  DCHECK_EQ(target_widget_->GetNativeWindow(), window);
+  if (key == kFrameImageActiveKey) {
+    SchedulePaint();
+  } else if (key == aura::client::kAvatarIconKey) {
+    gfx::ImageSkia* const avatar_icon =
+        window->GetProperty(aura::client::kAvatarIconKey);
+    SetAvatarIcon(avatar_icon ? *avatar_icon : gfx::ImageSkia());
+  } else if (key == ash::kFrameActiveColorKey ||
+             key == ash::kFrameInactiveColorKey) {
+    frame_header_->SetFrameColors(
+        window->GetProperty(ash::kFrameActiveColorKey),
+        window->GetProperty(ash::kFrameInactiveColorKey));
+  }
+}
+
+void HeaderView::OnWindowDestroying(aura::Window* window) {
+  window_observer_.Remove(window);
+}
+
 views::View* HeaderView::avatar_icon() const {
   return avatar_icon_;
 }
@@ -188,7 +255,7 @@
 }
 
 FrameCaptionButton* HeaderView::GetBackButton() {
-  return frame_header_->back_button();
+  return frame_header_->GetBackButton();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/ash/frame/header_view.h b/ash/frame/header_view.h
index 80455e1..e437deb 100644
--- a/ash/frame/header_view.h
+++ b/ash/frame/header_view.h
@@ -8,12 +8,15 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "ash/frame/default_frame_header.h"
+#include "ash/frame/custom_frame_header.h"
+#include "ash/frame/frame_header.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ash/public/interfaces/window_style.mojom.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "base/macros.h"
+#include "base/scoped_observer.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura/window_observer.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/views/view.h"
 
@@ -38,7 +41,8 @@
 // and on screen in immersive fullscreen.
 class ASH_EXPORT HeaderView : public views::View,
                               public ImmersiveFullscreenControllerDelegate,
-                              public TabletModeObserver {
+                              public TabletModeObserver,
+                              public aura::WindowObserver {
  public:
   // |target_widget| is the widget that the caption buttons act on.
   // |target_widget| is not necessarily the same as the widget the header is
@@ -75,10 +79,6 @@
 
   void UpdateCaptionButtons();
 
-  void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color);
-  SkColor GetActiveFrameColor() const;
-  SkColor GetInactiveFrameColor() const;
-
   // Called when the target widget show state changed.
   void OnShowStateChanged(ui::WindowShowState show_state);
 
@@ -91,6 +91,12 @@
   void OnTabletModeStarted() override;
   void OnTabletModeEnded() override;
 
+  // aura::WindowObserver:
+  void OnWindowPropertyChanged(aura::Window* window,
+                               const void* key,
+                               intptr_t old) override;
+  void OnWindowDestroying(aura::Window* window) override;
+
   FrameCaptionButtonContainerView* caption_button_container() {
     return caption_button_container_;
   }
@@ -100,10 +106,6 @@
   bool in_immersive_mode() const { return in_immersive_mode_; }
   bool is_revealed() const { return fullscreen_visible_fraction_ > 0.0; }
 
-  void set_title(const base::string16& title) {
-    frame_header_->set_title(title);
-  }
-
   void SetShouldPaintHeader(bool paint);
 
   FrameCaptionButton* GetBackButton();
@@ -120,8 +122,14 @@
   // The widget that the caption buttons act on.
   views::Widget* target_widget_;
 
-  // Helper for painting the header.
-  std::unique_ptr<DefaultFrameHeader> frame_header_;
+  std::unique_ptr<CustomFrameHeader::AppearanceProvider> appearance_provider_;
+
+  // Helper for painting the header. The exact type of FrameHeader will depend
+  // on the type of window: In Mash, Chrome Browser windows use
+  // CustomFrameHeader which is aware of theming. In classic Ash, Chrome Browser
+  // windows won't use HeaderView at all. In either configuration, non Browser
+  // windows will use DefaultFrameHeader.
+  std::unique_ptr<FrameHeader> frame_header_;
 
   views::ImageView* avatar_icon_;
 
@@ -142,6 +150,9 @@
 
   bool in_immersive_mode_ = false;
 
+  // Observes property changes to |target_widget_|'s window.
+  ScopedObserver<aura::Window, aura::WindowObserver> window_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(HeaderView);
 };
 
diff --git a/ash/frame/wide_frame_view.cc b/ash/frame/wide_frame_view.cc
index e3709b1..95d5138 100644
--- a/ash/frame/wide_frame_view.cc
+++ b/ash/frame/wide_frame_view.cc
@@ -105,11 +105,8 @@
 
   aura::Window* target_window = target->GetNativeWindow();
   target_window->AddObserver(this);
-  SkColor active = target_window->GetProperty(kFrameActiveColorKey);
-  SkColor inactive = target_window->GetProperty(kFrameInactiveColorKey);
   header_view_ = new HeaderView(target);
   AddChildView(header_view_);
-  header_view_->SetFrameColors(active, inactive);
   GetTargetHeaderView()->SetShouldPaintHeader(false);
 }
 
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc
index 0d4a2e9..8b7da85c 100644
--- a/ash/highlighter/highlighter_controller.cc
+++ b/ash/highlighter/highlighter_controller.cc
@@ -64,6 +64,16 @@
   Shell::Get()->RemovePreTargetHandler(this);
 }
 
+void HighlighterController::AddObserver(Observer* observer) {
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+}
+
+void HighlighterController::RemoveObserver(Observer* observer) {
+  DCHECK(observer);
+  observers_.RemoveObserver(observer);
+}
+
 void HighlighterController::SetExitCallback(base::OnceClosure exit_callback,
                                             bool require_success) {
   exit_callback_ = std::move(exit_callback);
@@ -89,6 +99,9 @@
     if (highlighter_view_ && !highlighter_view_->animating())
       DestroyPointerView();
   }
+  for (auto& observer : observers_)
+    observer.OnHighlighterEnabledChanged(enabled);
+
   if (client_)
     client_->HandleEnabledStateChange(enabled);
 }
diff --git a/ash/highlighter/highlighter_controller.h b/ash/highlighter/highlighter_controller.h
index 7136d0e..87b5b0f 100644
--- a/ash/highlighter/highlighter_controller.h
+++ b/ash/highlighter/highlighter_controller.h
@@ -12,6 +12,7 @@
 #include "ash/public/interfaces/highlighter_controller.mojom.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
 namespace base {
@@ -30,9 +31,24 @@
     : public fast_ink::FastInkPointerController,
       public mojom::HighlighterController {
  public:
+  // Interface for classes that wish to be notified with highlighter status.
+  class Observer {
+   public:
+    // Called when highlighter enabled status changes.
+    // TODO(warx): add a reason enum to distinguish the case of deselecting the
+    // tool and done with a stylus selection.
+    virtual void OnHighlighterEnabledChanged(bool enabled) {}
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
   HighlighterController();
   ~HighlighterController() override;
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Set the callback to exit the highlighter mode. If |require_success| is
   // true, the callback will be called only after a successful gesture
   // recognition. If |require_success| is false, the callback will be  called
@@ -115,6 +131,8 @@
   // Interface to highlighter controller client (chrome).
   mojom::HighlighterControllerClientPtr client_;
 
+  base::ObserverList<Observer> observers_;
+
   base::WeakPtrFactory<HighlighterController> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(HighlighterController);
diff --git a/ash/message_center/message_center_controller.h b/ash/message_center/message_center_controller.h
index cdacbb5..34fabf5 100644
--- a/ash/message_center/message_center_controller.h
+++ b/ash/message_center/message_center_controller.h
@@ -7,9 +7,9 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/interfaces/ash_message_center_controller.mojom.h"
-#include "ash/system/web_notification/fullscreen_notification_blocker.h"
-#include "ash/system/web_notification/inactive_user_notification_blocker.h"
-#include "ash/system/web_notification/session_state_notification_blocker.h"
+#include "ash/system/message_center/fullscreen_notification_blocker.h"
+#include "ash/system/message_center/inactive_user_notification_blocker.h"
+#include "ash/system/message_center/session_state_notification_blocker.h"
 #include "base/macros.h"
 #include "components/arc/common/notifications.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
diff --git a/ash/public/cpp/ash_switches.cc b/ash/public/cpp/ash_switches.cc
index 8836081..79c94b0 100644
--- a/ash/public/cpp/ash_switches.cc
+++ b/ash/public/cpp/ash_switches.cc
@@ -154,10 +154,6 @@
 // Hides all Message Center notification popups (toasts). Used for testing.
 const char kSuppressMessageCenterPopups[] = "suppress-message-center-popups";
 
-// By default we use classic IME (i.e. InputMethodChromeOS) in kMus. This flag
-// enables the IME service (i.e. InputMethodMus) instead.
-const char kUseIMEService[] = "use-ime-service";
-
 bool IsNightLightEnabled() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       kAshEnableNightLight);
diff --git a/ash/public/cpp/ash_switches.h b/ash/public/cpp/ash_switches.h
index 5807fe2..0b198ed6 100644
--- a/ash/public/cpp/ash_switches.h
+++ b/ash/public/cpp/ash_switches.h
@@ -61,7 +61,6 @@
 ASH_PUBLIC_EXPORT extern const char kShowWebUiLogin[];
 ASH_PUBLIC_EXPORT extern const char kSuppressMessageCenterPopups[];
 ASH_PUBLIC_EXPORT extern const char kTouchscreenUsableWhileScreenOff[];
-ASH_PUBLIC_EXPORT extern const char kUseIMEService[];
 
 ASH_PUBLIC_EXPORT bool IsDisplayMoveWindowAccelsEnabled();
 ASH_PUBLIC_EXPORT bool IsNightLightEnabled();
diff --git a/ash/public/cpp/config.h b/ash/public/cpp/config.h
index 4fb88c6..f375298 100644
--- a/ash/public/cpp/config.h
+++ b/ash/public/cpp/config.h
@@ -13,6 +13,8 @@
   CLASSIC,
 
   // Aura is backed by mus, but chrome and ash are still in the same process.
+  // TODO(jamescook): Remove this mode. We are switching to window service as a
+  // library, https://crbug.com/837684
   MUS,
 
   // Aura is backed by mus and chrome and ash are in separate processes. In this
diff --git a/ash/public/cpp/menu_struct_mojom_traits.h b/ash/public/cpp/menu_struct_mojom_traits.h
index a799efd..37aaf56 100644
--- a/ash/public/cpp/menu_struct_mojom_traits.h
+++ b/ash/public/cpp/menu_struct_mojom_traits.h
@@ -28,6 +28,8 @@
         return ash::mojom::MenuItemType::COMMAND;
       case ui::MenuModel::TYPE_SUBMENU:
         return ash::mojom::MenuItemType::SUBMENU;
+      case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+        return ash::mojom::MenuItemType::ACTIONABLE_SUBMENU;
     }
     NOTREACHED();
     return ash::mojom::MenuItemType::COMMAND;
@@ -51,6 +53,9 @@
       case ash::mojom::MenuItemType::SUBMENU:
         *out = ui::MenuModel::TYPE_SUBMENU;
         return true;
+      case ash::mojom::MenuItemType::ACTIONABLE_SUBMENU:
+        *out = ui::MenuModel::TYPE_ACTIONABLE_SUBMENU;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ash/public/cpp/menu_utils.cc b/ash/public/cpp/menu_utils.cc
index 81cd8891..516b9f2 100644
--- a/ash/public/cpp/menu_utils.cc
+++ b/ash/public/cpp/menu_utils.cc
@@ -26,8 +26,10 @@
     item->checked = model->IsItemCheckedAt(i);
     item->enabled = model->IsEnabledAt(i);
     item->radio_group_id = model->GetGroupIdAt(i);
-    if (item->type == ui::MenuModel::TYPE_SUBMENU)
+    if (item->type == ui::MenuModel::TYPE_SUBMENU ||
+        item->type == ui::MenuModel::TYPE_ACTIONABLE_SUBMENU) {
       item->submenu = GetMojoMenuItemsFromModel(model->GetSubmenuModelAt(i));
+    }
     gfx::Image icon;
     if (model->GetIconAt(i, &icon))
       item->image = icon.AsImageSkia();
@@ -59,12 +61,18 @@
         NOTREACHED() << "TYPE_BUTTON_ITEM is not yet supported.";
         break;
       case ui::MenuModel::TYPE_SUBMENU:
+      case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
         if (item->submenu.has_value()) {
           std::unique_ptr<ui::SimpleMenuModel> submenu =
               std::make_unique<ui::SimpleMenuModel>(delegate);
           PopulateMenuFromMojoMenuItems(submenu.get(), delegate,
                                         item->submenu.value(), submenus);
-          model->AddSubMenu(item->command_id, item->label, submenu.get());
+          if (item->type == ui::MenuModel::TYPE_SUBMENU) {
+            model->AddSubMenu(item->command_id, item->label, submenu.get());
+          } else {
+            model->AddActionableSubMenu(item->command_id, item->label,
+                                        submenu.get());
+          }
           submenus->push_back(std::move(submenu));
         }
         break;
@@ -83,7 +91,8 @@
   for (const mojom::MenuItemPtr& item : items) {
     if (item->command_id == command_id)
       return item;
-    if (item->type == ui::MenuModel::TYPE_SUBMENU &&
+    if ((item->type == ui::MenuModel::TYPE_SUBMENU ||
+         (item->type == ui::MenuModel::TYPE_ACTIONABLE_SUBMENU)) &&
         item->submenu.has_value()) {
       const mojom::MenuItemPtr& submenu_item =
           GetMenuItemByCommandId(item->submenu.value(), command_id);
diff --git a/ash/public/cpp/mus_property_mirror_ash.cc b/ash/public/cpp/mus_property_mirror_ash.cc
index 90189f8..01793527 100644
--- a/ash/public/cpp/mus_property_mirror_ash.cc
+++ b/ash/public/cpp/mus_property_mirror_ash.cc
@@ -63,6 +63,11 @@
     MirrorOwnedProperty(window, root_window, aura::client::kTitleKey);
   } else if (key == aura::client::kWindowIconKey) {
     MirrorOwnedProperty(window, root_window, aura::client::kWindowIconKey);
+  } else if (key == kFrameImageActiveKey) {
+    MirrorOwnedProperty(window, root_window, kFrameImageActiveKey);
+  } else if (key == kWindowTitleShownKey) {
+    root_window->SetProperty(kWindowTitleShownKey,
+                             window->GetProperty(kWindowTitleShownKey));
   }
 }
 
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index ae06aa6b..56d79821 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -8,6 +8,7 @@
 #include "ash/public/interfaces/window_pin_type.mojom.h"
 #include "ash/public/interfaces/window_state_type.mojom.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image_skia.h"
 
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(ASH_PUBLIC_EXPORT,
                                        ash::mojom::WindowPinType)
@@ -22,6 +23,9 @@
                              kBackdropWindowMode,
                              BackdropWindowMode::kAuto);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCanConsumeSystemKeysKey, false);
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::ImageSkia,
+                                   kFrameImageActiveKey,
+                                   nullptr);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideShelfWhenFullscreenKey, true);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kPanelAttachedKey, true);
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect,
@@ -46,5 +50,6 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(mojom::WindowStateType,
                              kWindowStateTypeKey,
                              mojom::WindowStateType::DEFAULT);
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowTitleShownKey, true);
 
 }  // namespace ash
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 620369e..dafb85f 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -18,6 +18,7 @@
 }
 
 namespace gfx {
+class ImageSkia;
 class Rect;
 }
 
@@ -47,6 +48,10 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kCanConsumeSystemKeysKey;
 
+// The frame's active image. Only set on themed windows.
+ASH_PUBLIC_EXPORT extern const aura::WindowProperty<gfx::ImageSkia*>* const
+    kFrameImageActiveKey;
+
 // Whether the shelf should be hidden when this window is put into fullscreen.
 // Exposed because some windows want to explicitly opt-out of this.
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
@@ -107,6 +112,11 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<
     mojom::WindowStateType>* const kWindowStateTypeKey;
 
+// Determines whether the window title should be drawn. For example, app and
+// non-tabbed, trusted source windows (such as Settings) will not show a title.
+ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
+    kWindowTitleShownKey;
+
 // Alphabetical sort.
 
 }  // namespace ash
diff --git a/ash/public/interfaces/menu.mojom b/ash/public/interfaces/menu.mojom
index 2986328f..971cd9c6 100644
--- a/ash/public/interfaces/menu.mojom
+++ b/ash/public/interfaces/menu.mojom
@@ -10,12 +10,13 @@
 // The types of menu items shown in shelf context and application list menus.
 // These values roughly match ui::MenuModel::ItemType (sans TYPE_BUTTON_ITEM).
 enum MenuItemType {
-  COMMAND,    // An item that performs an action when selected.
-  CHECK,      // An item that can be selected/checked to toggle a boolean state.
-  RADIO,      // An item that can be selected/checked among a group of choices.
-  SEPARATOR,  // An item that shows a horizontal line separator.
-  SUBMENU,    // An item that presents a submenu within another menu.
-};
+  COMMAND,    // Performs an action when selected.
+  CHECK,      // Can be selected/checked to toggle a boolean state.
+  RADIO,      // Can be selected/checked among a group of choices.
+  SEPARATOR,  // Shows a horizontal line separator.
+  SUBMENU,    // Presents a submenu within another menu.
+  ACTIONABLE_SUBMENU,  // A SUBMENU that is also a COMMAND.
+ };
 
 // MenuItems are used to populate application menus for shelf items.
 // Note: Some menus only support a subset of these item features (eg. no icons).
diff --git a/ash/public/interfaces/window_properties.mojom b/ash/public/interfaces/window_properties.mojom
index 0c54b00..07d02afc1 100644
--- a/ash/public/interfaces/window_properties.mojom
+++ b/ash/public/interfaces/window_properties.mojom
@@ -9,6 +9,10 @@
 const string kCanConsumeSystemKeys_Property =
   "ash:can-consume-system-keys";
 
+// A gfx::ImageSkia used to tell Mash the frame image to use for a custom
+// Browser theme.
+const string kFrameImageActive_Property = "ash:frame-image-active";
+
 // True if the shelf should be hidden when this window is put into fullscreen.
 // Exposed because some windows want to explicitly opt-out of this.
 const string kHideShelfWhenFullscreen_Property =
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 91535e0..1edf3c9 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -14,11 +14,6 @@
            SAME CONDITIONALS. -->
 
       <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_DISPLAY" file="cros/notification/display_notification_icon.png" />
-
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM" file="common/window_header_shade_bottom_inactive.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT" file="common/window_header_shade_left_inactive.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_RIGHT" file="common/window_header_shade_right_inactive.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_TOP" file="common/window_header_shade_top_inactive.png" />
     </structures>
   </release>
 </grit>
diff --git a/ash/resources/default_100_percent/common/window_header_shade_bottom_inactive.png b/ash/resources/default_100_percent/common/window_header_shade_bottom_inactive.png
deleted file mode 100644
index 69a3e01..0000000
--- a/ash/resources/default_100_percent/common/window_header_shade_bottom_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_header_shade_left_inactive.png b/ash/resources/default_100_percent/common/window_header_shade_left_inactive.png
deleted file mode 100644
index 06b9749..0000000
--- a/ash/resources/default_100_percent/common/window_header_shade_left_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_header_shade_right_inactive.png b/ash/resources/default_100_percent/common/window_header_shade_right_inactive.png
deleted file mode 100644
index 3dcc983..0000000
--- a/ash/resources/default_100_percent/common/window_header_shade_right_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_header_shade_top_inactive.png b/ash/resources/default_100_percent/common/window_header_shade_top_inactive.png
deleted file mode 100644
index 0be57ba..0000000
--- a/ash/resources/default_100_percent/common/window_header_shade_top_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_bottom_inactive.png b/ash/resources/default_200_percent/common/window_header_shade_bottom_inactive.png
deleted file mode 100644
index 5c6d4ff1..0000000
--- a/ash/resources/default_200_percent/common/window_header_shade_bottom_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_left_inactive.png b/ash/resources/default_200_percent/common/window_header_shade_left_inactive.png
deleted file mode 100644
index 91f40878..0000000
--- a/ash/resources/default_200_percent/common/window_header_shade_left_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_right_inactive.png b/ash/resources/default_200_percent/common/window_header_shade_right_inactive.png
deleted file mode 100644
index 2ab6d846..0000000
--- a/ash/resources/default_200_percent/common/window_header_shade_right_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_top_inactive.png b/ash/resources/default_200_percent/common/window_header_shade_top_inactive.png
deleted file mode 100644
index 576ad9b..0000000
--- a/ash/resources/default_200_percent/common/window_header_shade_top_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/session/session_controller_unittest.cc b/ash/session/session_controller_unittest.cc
index 3c65e76..20fb1d34 100644
--- a/ash/session/session_controller_unittest.cc
+++ b/ash/session/session_controller_unittest.cc
@@ -15,9 +15,9 @@
 #include "ash/session/session_observer.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/screen_security/screen_tray_item.h"
 #include "ash/system/tray/system_tray.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/window_util.h"
@@ -564,12 +564,12 @@
     capture_item_ = system_tray->GetScreenCaptureItem();
     EXPECT_TRUE(share_item_);
     EXPECT_TRUE(capture_item_);
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
   }
 
   void TearDown() override {
     RunAllPendingInMessageLoop();
-    WebNotificationTray::DisableAnimationsForTest(false);
+    NotificationTray::DisableAnimationsForTest(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 6bca366..7c88b70 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "ash/app_list/app_list_controller_impl.h"
+#include "ash/assistant/ash_assistant_controller.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/assistant_overlay.h"
@@ -132,12 +133,18 @@
         Shell::Get()->app_list_controller()->StartVoiceInteractionSession();
         assistant_overlay_->BurstAnimation();
         event->SetHandled();
+      } else if (chromeos::switches::IsAssistantEnabled()) {
+        // TODO: Handle overlay animation similarly to above. Also needs to
+        // factor in Assistant enabled state.
+        Shell::Get()->ash_assistant_controller()->StartInteraction();
+        event->SetHandled();
       } else {
         ImageButton::OnGestureEvent(event);
       }
       return;
     case ui::ET_GESTURE_LONG_TAP:
-      if (UseVoiceInteractionStyle()) {
+      if (UseVoiceInteractionStyle() ||
+          chromeos::switches::IsAssistantEnabled()) {
         // Also consume the long tap event. This happens after the user long
         // presses and lifts the finger. We already handled the long press
         // ignore the long tap to avoid bringing up the context menu again.
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index f16392d4..d54b8818 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -213,7 +213,7 @@
   class UpdateShelfObserver;
   friend class PanelLayoutManagerTest;
   friend class ShelfLayoutManagerTest;
-  friend class WebNotificationTrayTest;
+  friend class NotificationTrayTest;
 
   struct TargetBounds {
     TargetBounds();
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 5ea645d..fc619a5 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -34,7 +34,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_test_api.h"
-#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
@@ -261,7 +261,7 @@
     model_ = Shell::Get()->shelf_model();
     shelf_view_ = GetPrimaryShelf()->GetShelfViewForTesting();
 
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
 
     // The bounds should be big enough for 4 buttons + overflow chevron.
     shelf_view_->SetBounds(0, 0, 500, kShelfSize);
@@ -274,7 +274,7 @@
   }
 
   void TearDown() override {
-    WebNotificationTray::DisableAnimationsForTest(false);  // Reenable animation
+    NotificationTray::DisableAnimationsForTest(false);  // Reenable animation
     test_api_.reset();
     AshTestBase::TearDown();
   }
diff --git a/ash/shell.cc b/ash/shell.cc
index fda0577..17736e4 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -399,10 +399,7 @@
 
 // static
 bool Shell::ShouldUseIMEService() {
-  return Shell::GetAshConfig() == Config::MASH ||
-         (Shell::GetAshConfig() == Config::MUS &&
-          base::CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kUseIMEService));
+  return Shell::GetAshConfig() == Config::MASH;
 }
 
 // static
@@ -535,10 +532,10 @@
     Shelf::ForWindow(root)->UpdateVisibilityState();
 }
 
-WebNotificationTray* Shell::GetWebNotificationTray() {
+NotificationTray* Shell::GetNotificationTray() {
   return GetPrimaryRootWindowController()
       ->GetStatusAreaWidget()
-      ->web_notification_tray();
+      ->notification_tray();
 }
 
 bool Shell::HasPrimaryStatusArea() {
@@ -1091,10 +1088,6 @@
   app_list_controller_ = std::make_unique<AppListControllerImpl>();
   shelf_controller_ = std::make_unique<ShelfController>();
 
-  ash_assistant_controller_ = chromeos::switches::IsAssistantEnabled()
-                                  ? std::make_unique<AshAssistantController>()
-                                  : nullptr;
-
   magnifier_key_scroll_handler_ = MagnifierKeyScroller::CreateHandler();
   AddPreTargetHandler(magnifier_key_scroll_handler_.get());
   speech_feedback_handler_ = SpokenFeedbackToggler::CreateHandler();
@@ -1181,6 +1174,10 @@
   voice_interaction_controller_ =
       std::make_unique<VoiceInteractionController>();
 
+  ash_assistant_controller_ = chromeos::switches::IsAssistantEnabled()
+                                  ? std::make_unique<AshAssistantController>()
+                                  : nullptr;
+
   magnification_controller_ = std::make_unique<MagnificationController>();
   mru_window_tracker_ = std::make_unique<MruWindowTracker>();
 
diff --git a/ash/shell.h b/ash/shell.h
index 5740bea..75a7f6d 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -182,7 +182,7 @@
 class VpnList;
 class WallpaperController;
 class WaylandServerController;
-class WebNotificationTray;
+class NotificationTray;
 class WindowCycleController;
 class WindowPositioner;
 class WindowSelectorController;
@@ -556,8 +556,8 @@
   // TODO(jamescook): Move to Shelf.
   void UpdateShelfVisibility();
 
-  // Returns WebNotificationTray on the primary root window.
-  WebNotificationTray* GetWebNotificationTray();
+  // Returns NotificationTray on the primary root window.
+  NotificationTray* GetNotificationTray();
 
   // Does the primary display have status area?
   bool HasPrimaryStatusArea();
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index e162cb14..9ca7f74 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -13,8 +13,8 @@
 #include "ash/shell/example_factory.h"
 #include "ash/shell/panel_window.h"
 #include "ash/shell/toplevel_window.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/status_area_widget.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/wm/test_child_modal_parent.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/window.h"
@@ -303,7 +303,7 @@
 
     Shell::GetPrimaryRootWindowController()
         ->GetStatusAreaWidget()
-        ->web_notification_tray()
+        ->notification_tray()
         ->message_center()
         ->AddNotification(std::move(notification));
   } else if (sender == examples_button_) {
diff --git a/ash/sidebar/sidebar_widget.cc b/ash/sidebar/sidebar_widget.cc
index a2ab8ce..56ddcb0 100644
--- a/ash/sidebar/sidebar_widget.cc
+++ b/ash/sidebar/sidebar_widget.cc
@@ -12,9 +12,9 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/window.h"
diff --git a/ash/system/audio/unified_volume_slider_controller.cc b/ash/system/audio/unified_volume_slider_controller.cc
index 139df7f..17753d2 100644
--- a/ash/system/audio/unified_volume_slider_controller.cc
+++ b/ash/system/audio/unified_volume_slider_controller.cc
@@ -4,7 +4,12 @@
 
 #include "ash/system/audio/unified_volume_slider_controller.h"
 
+#include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "ash/system/audio/unified_volume_view.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 
 using chromeos::CrasAudioHandler;
 
@@ -21,8 +26,12 @@
 
 void UnifiedVolumeSliderController::ButtonPressed(views::Button* sender,
                                                   const ui::Event& event) {
-  CrasAudioHandler::Get()->SetOutputMute(
-      !CrasAudioHandler::Get()->IsOutputMuted());
+  bool mute_on = !CrasAudioHandler::Get()->IsOutputMuted();
+  if (mute_on)
+    base::RecordAction(base::UserMetricsAction("StatusArea_Audio_Muted"));
+  else
+    base::RecordAction(base::UserMetricsAction("StatusArea_Audio_Unmuted"));
+  CrasAudioHandler::Get()->SetOutputMute(mute_on);
 }
 
 void UnifiedVolumeSliderController::SliderValueChanged(
@@ -35,6 +44,11 @@
 
   const int level = value * 100;
 
+  if (level != CrasAudioHandler::Get()->GetOutputVolumePercent()) {
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_STATUS_AREA_CHANGED_VOLUME_MENU);
+  }
+
   CrasAudioHandler::Get()->SetOutputVolumePercent(level);
 
   // If the volume is above certain level and it's muted, it should be unmuted.
diff --git a/ash/system/audio/volume_view.cc b/ash/system/audio/volume_view.cc
index c524311..8507cb0 100644
--- a/ash/system/audio/volume_view.cc
+++ b/ash/system/audio/volume_view.cc
@@ -17,6 +17,8 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tri_view.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -256,6 +258,12 @@
   if (sender == icon_) {
     CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
     bool mute_on = !audio_handler->IsOutputMuted();
+
+    if (mute_on)
+      base::RecordAction(base::UserMetricsAction("StatusArea_Audio_Muted"));
+    else
+      base::RecordAction(base::UserMetricsAction("StatusArea_Audio_Unmuted"));
+
     audio_handler->SetOutputMute(mute_on);
     if (!mute_on)
       audio_handler->AdjustOutputVolumeToAudibleLevel();
diff --git a/ash/system/caps_lock_notification_controller_unittest.cc b/ash/system/caps_lock_notification_controller_unittest.cc
index b873a64..3a1917c 100644
--- a/ash/system/caps_lock_notification_controller_unittest.cc
+++ b/ash/system/caps_lock_notification_controller_unittest.cc
@@ -7,7 +7,7 @@
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/shell.h"
-#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/test/ash_test_base.h"
 
 namespace ash {
@@ -19,11 +19,11 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
   }
 
   void TearDown() override {
-    WebNotificationTray::DisableAnimationsForTest(false);
+    NotificationTray::DisableAnimationsForTest(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/system/web_notification/ash_popup_alignment_delegate.cc b/ash/system/message_center/ash_popup_alignment_delegate.cc
similarity index 98%
rename from ash/system/web_notification/ash_popup_alignment_delegate.cc
rename to ash/system/message_center/ash_popup_alignment_delegate.cc
index 79cef6b..fae8ba0 100644
--- a/ash/system/web_notification/ash_popup_alignment_delegate.cc
+++ b/ash/system/message_center/ash_popup_alignment_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
+#include "ash/system/message_center/ash_popup_alignment_delegate.h"
 
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
diff --git a/ash/system/web_notification/ash_popup_alignment_delegate.h b/ash/system/message_center/ash_popup_alignment_delegate.h
similarity index 91%
rename from ash/system/web_notification/ash_popup_alignment_delegate.h
rename to ash/system/message_center/ash_popup_alignment_delegate.h
index 7f87f770..3eed14b 100644
--- a/ash/system/web_notification/ash_popup_alignment_delegate.h
+++ b/ash/system/message_center/ash_popup_alignment_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
-#define ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_ASH_POPUP_ALIGNMENT_DELEGATE_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_ASH_POPUP_ALIGNMENT_DELEGATE_H_
 
 #include <stdint.h>
 
@@ -24,7 +24,7 @@
 
 class AshPopupAlignmentDelegateTest;
 class Shelf;
-class WebNotificationTrayTest;
+class NotificationTrayTest;
 
 // The PopupAlignmentDelegate subclass for Ash. It needs to handle alignment of
 // the shelf and its autohide state.
@@ -61,7 +61,7 @@
 
  private:
   friend class AshPopupAlignmentDelegateTest;
-  friend class WebNotificationTrayTest;
+  friend class NotificationTrayTest;
 
   // Get the current alignment of the shelf.
   ShelfAlignment GetAlignment() const;
@@ -92,4 +92,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_ASH_POPUP_ALIGNMENT_DELEGATE_H_
diff --git a/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc b/ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
similarity index 98%
rename from ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc
rename to ash/system/message_center/ash_popup_alignment_delegate_unittest.cc
index b52cfe863..d8f6c67 100644
--- a/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc
+++ b/ash/system/message_center/ash_popup_alignment_delegate_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 "ash/system/web_notification/ash_popup_alignment_delegate.h"
+#include "ash/system/message_center/ash_popup_alignment_delegate.h"
 
 #include <memory>
 #include <utility>
diff --git a/ash/system/web_notification/fullscreen_notification_blocker.cc b/ash/system/message_center/fullscreen_notification_blocker.cc
similarity index 96%
rename from ash/system/web_notification/fullscreen_notification_blocker.cc
rename to ash/system/message_center/fullscreen_notification_blocker.cc
index 29261df..381044f9 100644
--- a/ash/system/web_notification/fullscreen_notification_blocker.cc
+++ b/ash/system/message_center/fullscreen_notification_blocker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/web_notification/fullscreen_notification_blocker.h"
+#include "ash/system/message_center/fullscreen_notification_blocker.h"
 
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
diff --git a/ash/system/web_notification/fullscreen_notification_blocker.h b/ash/system/message_center/fullscreen_notification_blocker.h
similarity index 82%
rename from ash/system/web_notification/fullscreen_notification_blocker.h
rename to ash/system/message_center/fullscreen_notification_blocker.h
index 19de9cb..36f4c72 100644
--- a/ash/system/web_notification/fullscreen_notification_blocker.h
+++ b/ash/system/message_center/fullscreen_notification_blocker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_WEB_NOTIFICATION_FULLSCREEN_NOTIFICATION_BLOCKER_H_
-#define ASH_SYSTEM_WEB_NOTIFICATION_FULLSCREEN_NOTIFICATION_BLOCKER_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_FULLSCREEN_NOTIFICATION_BLOCKER_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_FULLSCREEN_NOTIFICATION_BLOCKER_H_
 
 #include "ash/shell_observer.h"
 #include "base/macros.h"
@@ -36,4 +36,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_WEB_NOTIFICATION_FULLSCREEN_NOTIFICATION_BLOCKER_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_FULLSCREEN_NOTIFICATION_BLOCKER_H_
diff --git a/ash/system/web_notification/inactive_user_notification_blocker.cc b/ash/system/message_center/inactive_user_notification_blocker.cc
similarity index 95%
rename from ash/system/web_notification/inactive_user_notification_blocker.cc
rename to ash/system/message_center/inactive_user_notification_blocker.cc
index 2ba18ba..9b432926 100644
--- a/ash/system/web_notification/inactive_user_notification_blocker.cc
+++ b/ash/system/message_center/inactive_user_notification_blocker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/web_notification/inactive_user_notification_blocker.h"
+#include "ash/system/message_center/inactive_user_notification_blocker.h"
 
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
diff --git a/ash/system/web_notification/inactive_user_notification_blocker.h b/ash/system/message_center/inactive_user_notification_blocker.h
similarity index 85%
rename from ash/system/web_notification/inactive_user_notification_blocker.h
rename to ash/system/message_center/inactive_user_notification_blocker.h
index f78ff43a..9f1f676 100644
--- a/ash/system/web_notification/inactive_user_notification_blocker.h
+++ b/ash/system/message_center/inactive_user_notification_blocker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
-#define ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
 
 #include <map>
 
@@ -43,4 +43,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
diff --git a/ash/system/web_notification/inactive_user_notification_blocker_unittest.cc b/ash/system/message_center/inactive_user_notification_blocker_unittest.cc
similarity index 98%
rename from ash/system/web_notification/inactive_user_notification_blocker_unittest.cc
rename to ash/system/message_center/inactive_user_notification_blocker_unittest.cc
index a1bc249c..8e802051 100644
--- a/ash/system/web_notification/inactive_user_notification_blocker_unittest.cc
+++ b/ash/system/message_center/inactive_user_notification_blocker_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 "ash/system/web_notification/inactive_user_notification_blocker.h"
+#include "ash/system/message_center/inactive_user_notification_blocker.h"
 
 #include "ash/message_center/message_center_controller.h"
 #include "ash/session/test_session_controller_client.h"
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/message_center/notification_tray.cc
similarity index 81%
rename from ash/system/web_notification/web_notification_tray.cc
rename to ash/system/message_center/notification_tray.cc
index 0961219b..66ee5a3 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/message_center/notification_tray.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 
 #include <memory>
 
@@ -17,12 +17,12 @@
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/message_center/ash_popup_alignment_delegate.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_utils.h"
-#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
 #include "base/auto_reset.h"
 #include "base/i18n/number_formatting.h"
 #include "base/i18n/rtl.h"
@@ -57,7 +57,8 @@
 
 constexpr size_t kMaximumNotificationNumber = 99;
 
-constexpr size_t kPaddingFromScreenTop = 8;  // in px. See crbug.com/754307.
+// in px. See https://crbug.com/754307.
+constexpr size_t kPaddingFromScreenTop = 8;
 
 constexpr float kBackgroundBlurRadius = 30.f;
 
@@ -68,13 +69,13 @@
 
 // Class to initialize and manage the WebNotificationBubble and
 // TrayBubbleWrapper instances for a bubble.
-class WebNotificationBubbleWrapper {
+class NotificationBubbleWrapper {
  public:
   // Takes ownership of |bubble| and creates |bubble_wrapper_|.
-  WebNotificationBubbleWrapper(WebNotificationTray* tray,
-                               TrayBackgroundView* anchor_tray,
-                               MessageCenterBubble* bubble,
-                               bool show_by_click) {
+  NotificationBubbleWrapper(NotificationTray* tray,
+                            TrayBackgroundView* anchor_tray,
+                            MessageCenterBubble* bubble,
+                            bool show_by_click) {
     bubble_.reset(bubble);
     views::TrayBubbleView::InitParams init_params;
     init_params.delegate = tray;
@@ -112,13 +113,13 @@
   std::unique_ptr<MessageCenterBubble> bubble_;
   std::unique_ptr<TrayBubbleWrapper> bubble_wrapper_;
 
-  DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper);
+  DISALLOW_COPY_AND_ASSIGN(NotificationBubbleWrapper);
 };
 
 class WebNotificationItem : public views::View, public gfx::AnimationDelegate {
  public:
   WebNotificationItem(gfx::AnimationContainer* container,
-                      WebNotificationTray* tray)
+                      NotificationTray* tray)
       : tray_(tray) {
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
@@ -127,7 +128,7 @@
 
     SetLayoutManager(std::make_unique<views::FillLayout>());
 
-    animation_.reset(new gfx::SlideAnimation(this));
+    animation_ = std::make_unique<gfx::SlideAnimation>(this);
     animation_->SetContainer(container);
     animation_->SetSlideDuration(kTrayItemAnimationDurationMS);
     animation_->SetTweenType(gfx::Tween::LINEAR);
@@ -217,16 +218,16 @@
 
   std::unique_ptr<gfx::SlideAnimation> animation_;
   bool delete_after_animation_ = false;
-  WebNotificationTray* tray_;
+  NotificationTray* tray_;
 
   DISALLOW_COPY_AND_ASSIGN(WebNotificationItem);
 };
 
-class WebNotificationImage : public WebNotificationItem {
+class NotificationImage : public WebNotificationItem {
  public:
-  WebNotificationImage(const gfx::ImageSkia& image,
-                       gfx::AnimationContainer* container,
-                       WebNotificationTray* tray)
+  NotificationImage(const gfx::ImageSkia& image,
+                    gfx::AnimationContainer* container,
+                    NotificationTray* tray)
       : WebNotificationItem(container, tray) {
     DCHECK(image.size() ==
            gfx::Size(kTrayItemInnerIconSize, kTrayItemInnerIconSize));
@@ -240,13 +241,12 @@
  private:
   views::ImageView* view_;
 
-  DISALLOW_COPY_AND_ASSIGN(WebNotificationImage);
+  DISALLOW_COPY_AND_ASSIGN(NotificationImage);
 };
 
-class WebNotificationLabel : public WebNotificationItem {
+class NotificationLabel : public WebNotificationItem {
  public:
-  WebNotificationLabel(gfx::AnimationContainer* container,
-                       WebNotificationTray* tray)
+  NotificationLabel(gfx::AnimationContainer* container, NotificationTray* tray)
       : WebNotificationItem(container, tray) {
     view_ = new views::Label();
     SetupLabelForTray(view_);
@@ -273,11 +273,11 @@
  private:
   views::Label* view_;
 
-  DISALLOW_COPY_AND_ASSIGN(WebNotificationLabel);
+  DISALLOW_COPY_AND_ASSIGN(NotificationLabel);
 };
 
-WebNotificationTray::WebNotificationTray(Shelf* shelf,
-                                         aura::Window* status_area_window)
+NotificationTray::NotificationTray(Shelf* shelf,
+                                   aura::Window* status_area_window)
     : TrayBackgroundView(shelf),
       status_area_window_(status_area_window),
       show_message_center_on_unlock_(false),
@@ -288,25 +288,28 @@
   SetInkDropMode(InkDropMode::ON);
   gfx::ImageSkia bell_image =
       CreateVectorIcon(kShelfNotificationsIcon, kShelfIconColor);
-  bell_icon_.reset(
-      new WebNotificationImage(bell_image, animation_container_.get(), this));
+  bell_icon_ = std::make_unique<NotificationImage>(
+      bell_image, animation_container_.get(), this);
   tray_container()->AddChildView(bell_icon_.get());
 
   gfx::ImageSkia quiet_mode_image =
       CreateVectorIcon(kNotificationCenterDoNotDisturbOnIcon,
                        kTrayItemInnerIconSize, kShelfIconColor);
-  quiet_mode_icon_.reset(new WebNotificationImage(
-      quiet_mode_image, animation_container_.get(), this));
+  quiet_mode_icon_ = std::make_unique<NotificationImage>(
+      quiet_mode_image, animation_container_.get(), this);
   tray_container()->AddChildView(quiet_mode_icon_.get());
 
-  counter_.reset(new WebNotificationLabel(animation_container_.get(), this));
+  counter_ =
+      std::make_unique<NotificationLabel>(animation_container_.get(), this);
   tray_container()->AddChildView(counter_.get());
 
-  message_center_ui_controller_.reset(new message_center::UiController(this));
-  popup_alignment_delegate_.reset(new AshPopupAlignmentDelegate(shelf));
-  popup_collection_.reset(new message_center::MessagePopupCollection(
+  message_center_ui_controller_ =
+      std::make_unique<message_center::UiController>(this);
+  popup_alignment_delegate_ =
+      std::make_unique<AshPopupAlignmentDelegate>(shelf);
+  popup_collection_ = std::make_unique<message_center::MessagePopupCollection>(
       message_center(), message_center_ui_controller_.get(),
-      popup_alignment_delegate_.get()));
+      popup_alignment_delegate_.get());
   display::Screen* screen = display::Screen::GetScreen();
   popup_alignment_delegate_->StartObserving(
       screen, screen->GetDisplayNearestWindow(status_area_window_));
@@ -315,7 +318,7 @@
   tray_container()->SetMargin(kTrayMainAxisInset, kTrayCrossAxisInset);
 }
 
-WebNotificationTray::~WebNotificationTray() {
+NotificationTray::~NotificationTray() {
   // Release any child views that might have back pointers before ~View().
   message_center_bubble_.reset();
   popup_alignment_delegate_.reset();
@@ -323,14 +326,14 @@
 }
 
 // static
-void WebNotificationTray::DisableAnimationsForTest(bool disable) {
+void NotificationTray::DisableAnimationsForTest(bool disable) {
   disable_animations_for_test = disable;
 }
 
 // Public methods.
 
-bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings,
-                                                    bool show_by_click) {
+bool NotificationTray::ShowMessageCenterInternal(bool show_settings,
+                                                 bool show_by_click) {
   if (!ShouldShowMessageCenter())
     return false;
 
@@ -348,7 +351,7 @@
         new MessageCenterBubble(message_center());
 
     // In the horizontal case, message center starts from the top of the shelf.
-    // In the vertical case, it starts from the bottom of WebNotificationTray.
+    // In the vertical case, it starts from the bottom of NotificationTray.
     const int max_height = (shelf()->IsHorizontalAlignment()
                                 ? shelf()->GetUserWorkAreaBounds().height()
                                 : GetBoundsInScreen().bottom() -
@@ -360,14 +363,14 @@
     if (show_settings)
       message_center_bubble->SetSettingsVisible();
 
-    // For vertical shelf alignments, anchor to the WebNotificationTray, but for
+    // For vertical shelf alignments, anchor to the NotificationTray, but for
     // horizontal (i.e. bottom) shelves, anchor to the system tray.
     TrayBackgroundView* anchor_tray = this;
     if (shelf()->IsHorizontalAlignment())
       anchor_tray = shelf()->GetSystemTrayAnchor();
 
-    message_center_bubble_.reset(new WebNotificationBubbleWrapper(
-        this, anchor_tray, message_center_bubble, show_by_click));
+    message_center_bubble_ = std::make_unique<NotificationBubbleWrapper>(
+        this, anchor_tray, message_center_bubble, show_by_click);
   }
 
   shelf()->UpdateAutoHideState();
@@ -375,11 +378,11 @@
   return true;
 }
 
-bool WebNotificationTray::ShowMessageCenter(bool show_by_click) {
+bool NotificationTray::ShowMessageCenter(bool show_by_click) {
   return ShowMessageCenterInternal(false /* show_settings */, show_by_click);
 }
 
-void WebNotificationTray::HideMessageCenter() {
+void NotificationTray::HideMessageCenter() {
   if ((switches::IsSidebarEnabled() && !IsMessageCenterVisible()) ||
       (!switches::IsSidebarEnabled() && !message_center_bubble()))
     return;
@@ -398,15 +401,15 @@
   shelf()->UpdateAutoHideState();
 }
 
-void WebNotificationTray::SetTrayBubbleHeight(int height) {
+void NotificationTray::SetTrayBubbleHeight(int height) {
   popup_alignment_delegate_->SetTrayBubbleHeight(height);
 }
 
-int WebNotificationTray::tray_bubble_height_for_test() const {
+int NotificationTray::tray_bubble_height_for_test() const {
   return popup_alignment_delegate_->tray_bubble_height_for_test();
 }
 
-bool WebNotificationTray::ShowPopups() {
+bool NotificationTray::ShowPopups() {
   if (IsMessageCenterVisible())
     return false;
 
@@ -414,19 +417,19 @@
   return true;
 }
 
-void WebNotificationTray::HidePopups() {
+void NotificationTray::HidePopups() {
   DCHECK(popup_collection_.get());
   popup_collection_->MarkAllPopupsShown();
 }
 
 // Private methods.
 
-bool WebNotificationTray::ShouldShowMessageCenter() const {
+bool NotificationTray::ShouldShowMessageCenter() const {
   // Hidden at login screen, during supervised user creation, etc.
   return Shell::Get()->session_controller()->ShouldShowNotificationTray();
 }
 
-bool WebNotificationTray::IsMessageCenterVisible() const {
+bool NotificationTray::IsMessageCenterVisible() const {
   if (switches::IsSidebarEnabled()) {
     Sidebar* sidebar =
         RootWindowController::ForWindow(GetWidget()->GetNativeView())
@@ -438,7 +441,7 @@
   }
 }
 
-void WebNotificationTray::UpdateAfterShelfAlignmentChange() {
+void NotificationTray::UpdateAfterShelfAlignmentChange() {
   TrayBackgroundView::UpdateAfterShelfAlignmentChange();
   // Destroy existing message center bubble so that it won't be reused.
   message_center_ui_controller_->HideMessageCenterBubble();
@@ -448,7 +451,7 @@
   message_center_ui_controller_->ShowPopupBubble();
 }
 
-void WebNotificationTray::UpdateAfterRootWindowBoundsChange(
+void NotificationTray::UpdateAfterRootWindowBoundsChange(
     const gfx::Rect& old_bounds,
     const gfx::Rect& new_bounds) {
   TrayBackgroundView::UpdateAfterRootWindowBoundsChange(old_bounds, new_bounds);
@@ -458,7 +461,7 @@
   message_center_ui_controller_->HideMessageCenterBubble();
 }
 
-void WebNotificationTray::AnchorUpdated() {
+void NotificationTray::AnchorUpdated() {
   if (message_center_bubble()) {
     UpdateClippingWindowBounds();
     shelf()->GetSystemTrayAnchor()->UpdateClippingWindowBounds();
@@ -473,14 +476,14 @@
   }
 }
 
-base::string16 WebNotificationTray::GetAccessibleNameForTray() {
+base::string16 NotificationTray::GetAccessibleNameForTray() {
   return l10n_util::GetStringFUTF16Int(
       IDS_MESSAGE_CENTER_ACCESSIBLE_NAME,
       static_cast<int>(message_center_ui_controller_->message_center()
                            ->NotificationCount()));
 }
 
-void WebNotificationTray::HideBubbleWithView(
+void NotificationTray::HideBubbleWithView(
     const views::TrayBubbleView* bubble_view) {
   if (message_center_bubble() &&
       bubble_view == message_center_bubble()->bubble_view()) {
@@ -490,28 +493,28 @@
   }
 }
 
-void WebNotificationTray::BubbleViewDestroyed() {
+void NotificationTray::BubbleViewDestroyed() {
   if (message_center_bubble())
     message_center_bubble()->bubble()->BubbleViewDestroyed();
 }
 
-void WebNotificationTray::OnMouseEnteredView() {}
+void NotificationTray::OnMouseEnteredView() {}
 
-void WebNotificationTray::OnMouseExitedView() {}
+void NotificationTray::OnMouseExitedView() {}
 
-base::string16 WebNotificationTray::GetAccessibleNameForBubble() {
+base::string16 NotificationTray::GetAccessibleNameForBubble() {
   return GetAccessibleNameForTray();
 }
 
-bool WebNotificationTray::ShouldEnableExtraKeyboardAccessibility() {
+bool NotificationTray::ShouldEnableExtraKeyboardAccessibility() {
   return Shell::Get()->accessibility_controller()->IsSpokenFeedbackEnabled();
 }
 
-void WebNotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
+void NotificationTray::HideBubble(const views::TrayBubbleView* bubble_view) {
   HideBubbleWithView(bubble_view);
 }
 
-bool WebNotificationTray::ShowNotifierSettings() {
+bool NotificationTray::ShowNotifierSettings() {
   if (IsMessageCenterVisible()) {
     if (switches::IsSidebarEnabled()) {
       Sidebar* sidebar =
@@ -529,17 +532,17 @@
                                    false /* show_by_click */);
 }
 
-void WebNotificationTray::OnMessageCenterContentsChanged() {
+void NotificationTray::OnMessageCenterContentsChanged() {
   // Do not update the tray contents directly. Multiple change events can happen
   // consecutively, and calling Update in the middle of those events will show
   // intermediate unread counts for a moment.
   should_update_tray_content_ = true;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::Bind(&WebNotificationTray::UpdateTrayContent, AsWeakPtr()));
+      base::BindOnce(&NotificationTray::UpdateTrayContent, AsWeakPtr()));
 }
 
-void WebNotificationTray::UpdateTrayContent() {
+void NotificationTray::UpdateTrayContent() {
   if (!should_update_tray_content_)
     return;
   should_update_tray_content_ = false;
@@ -566,7 +569,7 @@
     if (visible_small_icons_.count(notification->id()) != 0)
       continue;
 
-    auto item = std::make_unique<WebNotificationImage>(
+    auto item = std::make_unique<NotificationImage>(
         image.AsImageSkia(), animation_container_.get(), this);
     tray_container()->AddChildViewAt(item.get(), 0);
     item->SetVisible(true);
@@ -576,7 +579,7 @@
 
   // Remove unnecessary icons.
   for (const std::string& id : notification_ids) {
-    WebNotificationImage* item = visible_small_icons_[id].release();
+    NotificationImage* item = visible_small_icons_[id].release();
     visible_small_icons_.erase(id);
     item->HideAndDelete();
   }
@@ -606,7 +609,7 @@
   SchedulePaint();
 }
 
-void WebNotificationTray::ClickedOutsideBubble() {
+void NotificationTray::ClickedOutsideBubble() {
   // Only hide the message center
   if (!IsMessageCenterVisible())
     return;
@@ -614,7 +617,7 @@
   message_center_ui_controller_->HideMessageCenterBubble();
 }
 
-bool WebNotificationTray::PerformAction(const ui::Event& event) {
+bool NotificationTray::PerformAction(const ui::Event& event) {
   UserMetricsRecorder::RecordUserClickOnTray(
       LoginMetricsRecorder::TrayClickTarget::kNotificationTray);
   if (IsMessageCenterVisible())
@@ -624,31 +627,31 @@
   return true;
 }
 
-void WebNotificationTray::CloseBubble() {
+void NotificationTray::CloseBubble() {
   message_center_ui_controller_->HideMessageCenterBubble();
 }
 
-void WebNotificationTray::ShowBubble(bool show_by_click) {
+void NotificationTray::ShowBubble(bool show_by_click) {
   if (!IsMessageCenterVisible())
     message_center_ui_controller_->ShowMessageCenterBubble(show_by_click);
 }
 
-views::TrayBubbleView* WebNotificationTray::GetBubbleView() {
+views::TrayBubbleView* NotificationTray::GetBubbleView() {
   return message_center_bubble_ ? message_center_bubble_->bubble_view()
                                 : nullptr;
 }
 
-message_center::MessageCenter* WebNotificationTray::message_center() const {
+message_center::MessageCenter* NotificationTray::message_center() const {
   return message_center_ui_controller_->message_center();
 }
 
 // Methods for testing
 
-bool WebNotificationTray::IsPopupVisible() const {
+bool NotificationTray::IsPopupVisible() const {
   return message_center_ui_controller_->popups_visible();
 }
 
-MessageCenterBubble* WebNotificationTray::GetMessageCenterBubbleForTest() {
+MessageCenterBubble* NotificationTray::GetMessageCenterBubbleForTest() {
   if (!message_center_bubble())
     return nullptr;
   return static_cast<MessageCenterBubble*>(message_center_bubble()->bubble());
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/message_center/notification_tray.h
similarity index 67%
rename from ash/system/web_notification/web_notification_tray.h
rename to ash/system/message_center/notification_tray.h
index e2c18f6..1ed0312 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/message_center/notification_tray.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_WEB_NOTIFICATION_WEB_NOTIFICATION_TRAY_H_
-#define ASH_SYSTEM_WEB_NOTIFICATION_WEB_NOTIFICATION_TRAY_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_NOTIFICATION_TRAY_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_NOTIFICATION_TRAY_H_
 
 #include <memory>
 
@@ -25,29 +25,25 @@
 class MessageCenter;
 class MessagePopupCollection;
 class UiController;
-}
+}  // namespace message_center
 
 namespace ash {
 class AshPopupAlignmentDelegate;
 class MessageCenterBubble;
-class WebNotificationBubbleWrapper;
-class WebNotificationImage;
-class WebNotificationLabel;
+class NotificationBubbleWrapper;
+class NotificationImage;
+class NotificationLabel;
 
-// Status area tray for showing browser and app notifications. This hosts
-// a MessageCenter class which manages the notification list. This class
-// contains the Ash specific tray implementation.
-//
-// Note: These are not related to system notifications (i.e NotificationView
-// generated by SystemTrayItem). Visibility of one notification type or other
-// is controlled by StatusAreaWidget.
-class ASH_EXPORT WebNotificationTray
+// Status area tray for showing System, Chrome App, Web and ARC++ app
+// notifications. This hosts a MessageCenter class which manages the
+// notification list. This class contains the Ash specific tray implementation.
+class ASH_EXPORT NotificationTray
     : public TrayBackgroundView,
       public message_center::UiDelegate,
-      public base::SupportsWeakPtr<WebNotificationTray> {
+      public base::SupportsWeakPtr<NotificationTray> {
  public:
-  WebNotificationTray(Shelf* shelf, aura::Window* status_area_window);
-  ~WebNotificationTray() override;
+  NotificationTray(Shelf* shelf, aura::Window* status_area_window);
+  ~NotificationTray() override;
 
   static void DisableAnimationsForTest(bool disable);
 
@@ -94,19 +90,19 @@
   message_center::MessageCenter* message_center() const;
 
  private:
-  friend class WebNotificationTrayTest;
+  friend class NotificationTrayTest;
 
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotifications);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, WebNotificationPopupBubble);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest,
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, Notifications);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, NotificationPopupBubble);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest,
                            ManyMessageCenterNotifications);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, ManyPopupNotifications);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, PopupShownOnBothDisplays);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, PopupAndSystemTray);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, PopupAndAutoHideShelf);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, VisibleSmallIcon);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, QuietModeIcon);
-  FRIEND_TEST_ALL_PREFIXES(WebNotificationTrayTest, CloseOnActivation);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, ManyPopupNotifications);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, PopupShownOnBothDisplays);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, PopupAndSystemTray);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, PopupAndAutoHideShelf);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, VisibleSmallIcon);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, QuietModeIcon);
+  FRIEND_TEST_ALL_PREFIXES(NotificationTrayTest, CloseOnActivation);
 
   void UpdateTrayContent();
 
@@ -120,7 +116,7 @@
   // the message center.
   bool ShouldShowMessageCenter() const;
 
-  WebNotificationBubbleWrapper* message_center_bubble() const {
+  NotificationBubbleWrapper* message_center_bubble() const {
     return message_center_bubble_.get();
   }
 
@@ -130,16 +126,16 @@
 
   aura::Window* status_area_window_;
   std::unique_ptr<message_center::UiController> message_center_ui_controller_;
-  std::unique_ptr<WebNotificationBubbleWrapper> message_center_bubble_;
+  std::unique_ptr<NotificationBubbleWrapper> message_center_bubble_;
   std::unique_ptr<message_center::MessagePopupCollection> popup_collection_;
   std::unique_ptr<views::View> bell_icon_;
   std::unique_ptr<views::View> quiet_mode_icon_;
-  std::unique_ptr<WebNotificationLabel> counter_;
+  std::unique_ptr<NotificationLabel> counter_;
 
   scoped_refptr<gfx::AnimationContainer> animation_container_ =
       new gfx::AnimationContainer();
 
-  std::unordered_map<std::string, std::unique_ptr<WebNotificationImage>>
+  std::unordered_map<std::string, std::unique_ptr<NotificationImage>>
       visible_small_icons_;
 
   bool show_message_center_on_unlock_;
@@ -148,9 +144,9 @@
 
   std::unique_ptr<AshPopupAlignmentDelegate> popup_alignment_delegate_;
 
-  DISALLOW_COPY_AND_ASSIGN(WebNotificationTray);
+  DISALLOW_COPY_AND_ASSIGN(NotificationTray);
 };
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_WEB_NOTIFICATION_WEB_NOTIFICATION_TRAY_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_NOTIFICATION_TRAY_H_
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/message_center/notification_tray_unittest.cc
similarity index 90%
rename from ash/system/web_notification/web_notification_tray_unittest.cc
rename to ash/system/message_center/notification_tray_unittest.cc
index f013a4e..cebfad4 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/message_center/notification_tray_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 "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 
 #include <memory>
 #include <utility>
@@ -15,16 +15,17 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/ash_popup_alignment_delegate.h"
 #include "ash/system/screen_layout_observer.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/system/tray/tray_container.h"
-#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/display/display.h"
@@ -50,16 +51,15 @@
 
 namespace {
 
-WebNotificationTray* GetTray() {
-  return StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-      ->web_notification_tray();
+NotificationTray* GetTray() {
+  return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->notification_tray();
 }
 
-WebNotificationTray* GetSecondaryTray() {
+NotificationTray* GetSecondaryTray() {
   StatusAreaWidget* status_area_widget =
       StatusAreaWidgetTestHelper::GetSecondaryStatusAreaWidget();
   if (status_area_widget)
-    return status_area_widget->web_notification_tray();
+    return status_area_widget->notification_tray();
   return NULL;
 }
 
@@ -89,10 +89,10 @@
 
 }  // namespace
 
-class WebNotificationTrayTest : public AshTestBase {
+class NotificationTrayTest : public AshTestBase {
  public:
-  WebNotificationTrayTest() = default;
-  ~WebNotificationTrayTest() override = default;
+  NotificationTrayTest() = default;
+  ~NotificationTrayTest() override = default;
 
  protected:
   void AddNotification(const std::string& id) {
@@ -130,7 +130,7 @@
     return GetPopupWorkAreaBottomForTray(GetTray());
   }
 
-  int GetPopupWorkAreaBottomForTray(WebNotificationTray* tray) {
+  int GetPopupWorkAreaBottomForTray(NotificationTray* tray) {
     return tray->popup_alignment_delegate_->GetWorkArea().bottom();
   }
 
@@ -146,10 +146,10 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WebNotificationTrayTest);
+  DISALLOW_COPY_AND_ASSIGN(NotificationTrayTest);
 };
 
-TEST_F(WebNotificationTrayTest, WebNotifications) {
+TEST_F(NotificationTrayTest, Notifications) {
   // TODO(mukai): move this test case to ui/message_center.
   ASSERT_TRUE(GetWidget());
 
@@ -179,7 +179,7 @@
   EXPECT_FALSE(GetMessageCenter()->FindVisibleNotificationById("test_id3"));
 }
 
-TEST_F(WebNotificationTrayTest, WebNotificationPopupBubble) {
+TEST_F(NotificationTrayTest, NotificationPopupBubble) {
   // TODO(mukai): move this test case to ui/message_center.
   ASSERT_TRUE(GetWidget());
 
@@ -214,7 +214,7 @@
 
 using message_center::NotificationList;
 
-TEST_F(WebNotificationTrayTest, ManyMessageCenterNotifications) {
+TEST_F(NotificationTrayTest, ManyMessageCenterNotifications) {
   // Add the max visible notifications +1, ensure the correct visible number.
   size_t notifications_to_add = MessageCenterView::kMaxVisibleNotifications + 1;
   for (size_t i = 0; i < notifications_to_add; ++i) {
@@ -225,7 +225,7 @@
       GetTray()->message_center_ui_controller_->ShowMessageCenterBubble(
           false /* show_by_click */);
   EXPECT_TRUE(shown);
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(GetTray()->message_center_bubble() != NULL);
   EXPECT_EQ(notifications_to_add, GetMessageCenter()->NotificationCount());
   EXPECT_EQ(
@@ -233,7 +233,7 @@
       GetTray()->GetMessageCenterBubbleForTest()->NumMessageViewsForTest());
 }
 
-TEST_F(WebNotificationTrayTest, ManyPopupNotifications) {
+TEST_F(NotificationTrayTest, ManyPopupNotifications) {
   // Add the max visible popup notifications +1, ensure the correct num visible.
   size_t notifications_to_add =
       message_center::kMaxVisiblePopupNotifications + 1;
@@ -250,7 +250,7 @@
 }
 
 // Verifies if the notification appears on both displays when extended mode.
-TEST_F(WebNotificationTrayTest, PopupShownOnBothDisplays) {
+TEST_F(NotificationTrayTest, PopupShownOnBothDisplays) {
   Shell::Get()->screen_layout_observer()->set_show_notifications_for_testing(
       true);
 
@@ -269,7 +269,7 @@
   // OnNativeDisplaysChanged() creates the display notifications, so popup is
   // visible.
   EXPECT_TRUE(GetTray()->IsPopupVisible());
-  WebNotificationTray* secondary_tray = GetSecondaryTray();
+  NotificationTray* secondary_tray = GetSecondaryTray();
   ASSERT_TRUE(secondary_tray);
   EXPECT_TRUE(secondary_tray->IsPopupVisible());
 
@@ -306,7 +306,7 @@
 // PopupAndSystemTray may fail in platforms other than ChromeOS because the
 // RootWindow's bound can be bigger than display::Display's work area so that
 // openingsystem tray doesn't affect at all the work area of popups.
-TEST_F(WebNotificationTrayTest, PopupAndSystemTray) {
+TEST_F(NotificationTrayTest, PopupAndSystemTray) {
   GetSystemTray()->AddTrayItem(std::make_unique<TestItem>());
 
   AddNotification("test_id");
@@ -322,7 +322,7 @@
   EXPECT_GT(bottom, bottom_with_tray);
 }
 
-TEST_F(WebNotificationTrayTest, PopupAndAutoHideShelf) {
+TEST_F(NotificationTrayTest, PopupAndAutoHideShelf) {
   AddNotification("test_id");
   EXPECT_TRUE(GetTray()->IsPopupVisible());
   int bottom = GetPopupWorkAreaBottom();
@@ -355,7 +355,7 @@
   EXPECT_GT(bottom_auto_shown, bottom_with_tray);
 }
 
-TEST_F(WebNotificationTrayTest, PopupAndFullscreen) {
+TEST_F(NotificationTrayTest, PopupAndFullscreen) {
   AddNotification("test_id");
   EXPECT_TRUE(IsPopupVisible());
   int bottom = GetPopupWorkAreaBottom();
@@ -374,7 +374,7 @@
   wm::GetWindowState(widget->GetNativeWindow())
       ->SetHideShelfWhenFullscreen(false);
   widget->SetFullscreen(true);
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // The work area for auto-hidden status of fullscreen is a bit larger
   // since it doesn't even have the 3-pixel width.
@@ -399,7 +399,7 @@
   EXPECT_EQ(bottom_auto_hidden, GetPopupWorkAreaBottom());
 }
 
-TEST_F(WebNotificationTrayTest, PopupAndSystemTrayMultiDisplay) {
+TEST_F(NotificationTrayTest, PopupAndSystemTrayMultiDisplay) {
   UpdateDisplay("800x600,600x400");
 
   AddNotification("test_id");
@@ -414,7 +414,7 @@
   EXPECT_EQ(bottom_second, GetPopupWorkAreaBottomForTray(GetSecondaryTray()));
 }
 
-TEST_F(WebNotificationTrayTest, VisibleSmallIcon) {
+TEST_F(NotificationTrayTest, VisibleSmallIcon) {
   EXPECT_EQ(0u, GetTray()->visible_small_icons_.size());
   EXPECT_EQ(3, GetTray()->tray_container()->child_count());
   std::unique_ptr<message_center::Notification> notification =
@@ -429,23 +429,23 @@
           message_center::RichNotificationData(), nullptr /* delegate */);
   notification->set_small_image(gfx::test::CreateImage(18, 18));
   GetMessageCenter()->AddNotification(std::move(notification));
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, GetTray()->visible_small_icons_.size());
   EXPECT_EQ(4, GetTray()->tray_container()->child_count());
 }
 
-TEST_F(WebNotificationTrayTest, QuietModeIcon) {
-  WebNotificationTray::DisableAnimationsForTest(true);
+TEST_F(NotificationTrayTest, QuietModeIcon) {
+  NotificationTray::DisableAnimationsForTest(true);
 
   AddNotification("test");
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // There is a notification, so no bell & quiet mode icons are shown.
   EXPECT_FALSE(GetTray()->bell_icon_->visible());
   EXPECT_FALSE(GetTray()->quiet_mode_icon_->visible());
 
   GetMessageCenter()->SetQuietMode(true);
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // If there is a notification, setting quiet mode shouldn't change tray icons.
   EXPECT_FALSE(GetTray()->bell_icon_->visible());
@@ -454,27 +454,27 @@
   GetMessageCenter()->SetQuietMode(false);
   GetMessageCenter()->RemoveAllNotifications(
       false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // If there is no notification, bell icon should be shown.
   EXPECT_TRUE(GetTray()->bell_icon_->visible());
   EXPECT_FALSE(GetTray()->quiet_mode_icon_->visible());
 
   GetMessageCenter()->SetQuietMode(true);
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // If there is no notification and quiet mode is set, it should show quiet
   // mode icon.
   EXPECT_FALSE(GetTray()->bell_icon_->visible());
   EXPECT_TRUE(GetTray()->quiet_mode_icon_->visible());
 
-  WebNotificationTray::DisableAnimationsForTest(false);
+  NotificationTray::DisableAnimationsForTest(false);
 }
 
 // Makes sure that the system tray bubble closes when another window is
 // activated, and does not crash regardless of the initial activation state.
-TEST_F(WebNotificationTrayTest, CloseOnActivation) {
-  WebNotificationTray* tray = GetTray();
+TEST_F(NotificationTrayTest, CloseOnActivation) {
+  NotificationTray* tray = GetTray();
 
   // Show the web notification bubble.
   tray->ShowBubble(false /* show_by_click */);
@@ -489,7 +489,7 @@
   EXPECT_FALSE(tray->message_center_bubble());
 
   // Wait for bubble to actually close.
-  RunAllPendingInMessageLoop();
+  base::RunLoop().RunUntilIdle();
 
   // Show a second widget.
   std::unique_ptr<views::Widget> second_widget(CreateTestWidget());
diff --git a/ash/system/web_notification/session_state_notification_blocker.cc b/ash/system/message_center/session_state_notification_blocker.cc
similarity index 97%
rename from ash/system/web_notification/session_state_notification_blocker.cc
rename to ash/system/message_center/session_state_notification_blocker.cc
index e01c4b3..4f3bd1a 100644
--- a/ash/system/web_notification/session_state_notification_blocker.cc
+++ b/ash/system/message_center/session_state_notification_blocker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/web_notification/session_state_notification_blocker.h"
+#include "ash/system/message_center/session_state_notification_blocker.h"
 
 #include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller.h"
diff --git a/ash/system/web_notification/session_state_notification_blocker.h b/ash/system/message_center/session_state_notification_blocker.h
similarity index 88%
rename from ash/system/web_notification/session_state_notification_blocker.h
rename to ash/system/message_center/session_state_notification_blocker.h
index 0a51375..495a757 100644
--- a/ash/system/web_notification/session_state_notification_blocker.h
+++ b/ash/system/message_center/session_state_notification_blocker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_WEB_NOTIFICATION_SESSION_STATE_NOTIFICATION_BLOCKER_H_
-#define ASH_SYSTEM_WEB_NOTIFICATION_SESSION_STATE_NOTIFICATION_BLOCKER_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_SESSION_STATE_NOTIFICATION_BLOCKER_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_SESSION_STATE_NOTIFICATION_BLOCKER_H_
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
@@ -47,4 +47,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_WEB_NOTIFICATION_SESSION_STATE_NOTIFICATION_BLOCKER_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_SESSION_STATE_NOTIFICATION_BLOCKER_H_
diff --git a/ash/system/web_notification/session_state_notification_blocker_unittest.cc b/ash/system/message_center/session_state_notification_blocker_unittest.cc
similarity index 98%
rename from ash/system/web_notification/session_state_notification_blocker_unittest.cc
rename to ash/system/message_center/session_state_notification_blocker_unittest.cc
index f87f064..96f1a6b5 100644
--- a/ash/system/web_notification/session_state_notification_blocker_unittest.cc
+++ b/ash/system/message_center/session_state_notification_blocker_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 "ash/system/web_notification/session_state_notification_blocker.h"
+#include "ash/system/message_center/session_state_notification_blocker.h"
 
 #include <memory>
 
diff --git a/ash/system/screen_layout_observer_unittest.cc b/ash/system/screen_layout_observer_unittest.cc
index 126a4f0..51e37c3f8 100644
--- a/ash/system/screen_layout_observer_unittest.cc
+++ b/ash/system/screen_layout_observer_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/tray/system_tray.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
@@ -39,11 +39,11 @@
  protected:
   void SetUp() override {
     AshTestBase::SetUp();
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
   }
 
   void TearDown() override {
-    WebNotificationTray::DisableAnimationsForTest(false);
+    NotificationTray::DisableAnimationsForTest(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 40084fe..5d1e8036 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -13,6 +13,7 @@
 #include "ash/system/dictation/dictation_button_tray.h"
 #include "ash/system/flag_warning/flag_warning_tray.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/overview/overview_button_tray.h"
 #include "ash/system/palette/palette_tray.h"
 #include "ash/system/session/logout_button_tray.h"
@@ -20,7 +21,6 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "base/command_line.h"
 #include "base/i18n/time_formatting.h"
 #include "ui/display/display.h"
@@ -60,9 +60,9 @@
 
   // Must happen after the widget is initialized so the native window exists.
   if (!features::IsSystemTrayUnifiedEnabled()) {
-    web_notification_tray_ =
-        std::make_unique<WebNotificationTray>(shelf_, GetNativeWindow());
-    status_area_widget_delegate_->AddChildView(web_notification_tray_.get());
+    notification_tray_ =
+        std::make_unique<NotificationTray>(shelf_, GetNativeWindow());
+    status_area_widget_delegate_->AddChildView(notification_tray_.get());
   }
 
   palette_tray_ = std::make_unique<PaletteTray>(shelf_);
@@ -103,9 +103,9 @@
   status_area_widget_delegate_->UpdateLayout();
 
   // Initialize after all trays have been created.
-  if (web_notification_tray_) {
-    system_tray_->InitializeTrayItems(web_notification_tray_.get());
-    web_notification_tray_->Initialize();
+  if (notification_tray_) {
+    system_tray_->InitializeTrayItems(notification_tray_.get());
+    notification_tray_->Initialize();
   } else {
     system_tray_->InitializeTrayItems(nullptr);
   }
@@ -128,8 +128,8 @@
 StatusAreaWidget::~StatusAreaWidget() {
   system_tray_->Shutdown();
 
-  web_notification_tray_.reset();
-  // Must be destroyed after |web_notification_tray_|.
+  notification_tray_.reset();
+  // Must be destroyed after |notification_tray_|.
   system_tray_.reset();
   unified_system_tray_.reset();
   ime_menu_tray_.reset();
@@ -147,8 +147,8 @@
 
 void StatusAreaWidget::UpdateAfterShelfAlignmentChange() {
   system_tray_->UpdateAfterShelfAlignmentChange();
-  if (web_notification_tray_)
-    web_notification_tray_->UpdateAfterShelfAlignmentChange();
+  if (notification_tray_)
+    notification_tray_->UpdateAfterShelfAlignmentChange();
   logout_button_tray_->UpdateAfterShelfAlignmentChange();
   virtual_keyboard_tray_->UpdateAfterShelfAlignmentChange();
   ime_menu_tray_->UpdateAfterShelfAlignmentChange();
@@ -206,14 +206,13 @@
 
 bool StatusAreaWidget::IsMessageBubbleShown() const {
   return system_tray_->IsSystemBubbleVisible() ||
-         (web_notification_tray_ &&
-          web_notification_tray_->IsMessageCenterVisible());
+         (notification_tray_ && notification_tray_->IsMessageCenterVisible());
 }
 
 void StatusAreaWidget::SchedulePaint() {
   status_area_widget_delegate_->SchedulePaint();
-  if (web_notification_tray_)
-    web_notification_tray_->SchedulePaint();
+  if (notification_tray_)
+    notification_tray_->SchedulePaint();
   system_tray_->SchedulePaint();
   virtual_keyboard_tray_->SchedulePaint();
   logout_button_tray_->SchedulePaint();
@@ -241,8 +240,8 @@
 }
 
 void StatusAreaWidget::UpdateShelfItemBackground(SkColor color) {
-  if (web_notification_tray_)
-    web_notification_tray_->UpdateShelfItemBackground(color);
+  if (notification_tray_)
+    notification_tray_->UpdateShelfItemBackground(color);
   system_tray_->UpdateShelfItemBackground(color);
   virtual_keyboard_tray_->UpdateShelfItemBackground(color);
   ime_menu_tray_->UpdateShelfItemBackground(color);
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 8e78818..8db1bbf 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -30,7 +30,7 @@
 class UnifiedSystemTray;
 class TrayBackgroundView;
 class VirtualKeyboardTray;
-class WebNotificationTray;
+class NotificationTray;
 
 // Widget showing the system tray, notification tray, and other tray views in
 // the bottom-right of the screen. Exists separately from ShelfView/ShelfWidget
@@ -71,9 +71,7 @@
   UnifiedSystemTray* unified_system_tray() {
     return unified_system_tray_.get();
   }
-  WebNotificationTray* web_notification_tray() {
-    return web_notification_tray_.get();
-  }
+  NotificationTray* notification_tray() { return notification_tray_.get(); }
   DictationButtonTray* dictation_button_tray() {
     return dictation_button_tray_.get();
   }
@@ -129,7 +127,7 @@
   std::unique_ptr<DictationButtonTray> dictation_button_tray_;
   std::unique_ptr<SystemTray> system_tray_;
   std::unique_ptr<UnifiedSystemTray> unified_system_tray_;
-  std::unique_ptr<WebNotificationTray> web_notification_tray_;
+  std::unique_ptr<NotificationTray> notification_tray_;
   std::unique_ptr<LogoutButtonTray> logout_button_tray_;
   std::unique_ptr<PaletteTray> palette_tray_;
   std::unique_ptr<VirtualKeyboardTray> virtual_keyboard_tray_;
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index 0825928..cbefd86 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/system/ime_menu/ime_menu_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/overview/overview_button_tray.h"
 #include "ash/system/palette/palette_tray.h"
 #include "ash/system/session/logout_button_tray.h"
@@ -19,7 +20,6 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
 #include "base/test/scoped_feature_list.h"
@@ -47,20 +47,20 @@
   // Default trays are constructed.
   EXPECT_TRUE(status->overview_button_tray());
   EXPECT_TRUE(status->system_tray());
-  EXPECT_TRUE(status->web_notification_tray());
+  EXPECT_TRUE(status->notification_tray());
   EXPECT_TRUE(status->logout_button_tray_for_testing());
   EXPECT_TRUE(status->ime_menu_tray());
   EXPECT_TRUE(status->virtual_keyboard_tray_for_testing());
   EXPECT_TRUE(status->palette_tray());
 
-  // Needed because WebNotificationTray updates its initial visibility
+  // Needed because NotificationTray updates its initial visibility
   // asynchronously.
   RunAllPendingInMessageLoop();
 
   // Default trays are visible.
   EXPECT_FALSE(status->overview_button_tray()->visible());
   EXPECT_TRUE(status->system_tray()->visible());
-  EXPECT_TRUE(status->web_notification_tray()->visible());
+  EXPECT_TRUE(status->notification_tray()->visible());
   EXPECT_FALSE(status->logout_button_tray_for_testing()->visible());
   EXPECT_FALSE(status->ime_menu_tray()->visible());
   EXPECT_FALSE(status->virtual_keyboard_tray_for_testing()->visible());
@@ -138,19 +138,19 @@
   // Default trays are constructed.
   ASSERT_TRUE(status->overview_button_tray());
   ASSERT_TRUE(status->system_tray());
-  ASSERT_TRUE(status->web_notification_tray());
+  ASSERT_TRUE(status->notification_tray());
   ASSERT_TRUE(status->logout_button_tray_for_testing());
   ASSERT_TRUE(status->ime_menu_tray());
   ASSERT_TRUE(status->virtual_keyboard_tray_for_testing());
 
-  // Needed because WebNotificationTray updates its initial visibility
+  // Needed because NotificationTray updates its initial visibility
   // asynchronously.
   RunAllPendingInMessageLoop();
 
   // Default trays are visible.
   ASSERT_FALSE(status->overview_button_tray()->visible());
   ASSERT_TRUE(status->system_tray()->visible());
-  ASSERT_TRUE(status->web_notification_tray()->visible());
+  ASSERT_TRUE(status->notification_tray()->visible());
   ASSERT_FALSE(status->logout_button_tray_for_testing()->visible());
   ASSERT_FALSE(status->ime_menu_tray()->visible());
   ASSERT_FALSE(status->virtual_keyboard_tray_for_testing()->visible());
@@ -162,7 +162,7 @@
 
   // A tab key event will move focus to web notification tray.
   GenerateTabEvent(false);
-  EXPECT_EQ(status->web_notification_tray(), focus_manager->GetFocusedView());
+  EXPECT_EQ(status->notification_tray(), focus_manager->GetFocusedView());
   EXPECT_EQ(0, test_observer_->focus_out_count());
   EXPECT_EQ(0, test_observer_->reverse_focus_out_count());
 
@@ -176,7 +176,7 @@
   // A reverse tab key event will send reverse FocusOut event, since we are not
   // handling this event, focus will still be moved to web notification tray.
   GenerateTabEvent(true);
-  EXPECT_EQ(status->web_notification_tray(), focus_manager->GetFocusedView());
+  EXPECT_EQ(status->notification_tray(), focus_manager->GetFocusedView());
   EXPECT_EQ(1, test_observer_->focus_out_count());
   EXPECT_EQ(1, test_observer_->reverse_focus_out_count());
 }
diff --git a/ash/system/tray/interacted_by_tap_recorder.cc b/ash/system/tray/interacted_by_tap_recorder.cc
new file mode 100644
index 0000000..30e77a8f
--- /dev/null
+++ b/ash/system/tray/interacted_by_tap_recorder.cc
@@ -0,0 +1,27 @@
+// 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/tray/interacted_by_tap_recorder.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "ui/events/event.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+InteractedByTapRecorder::InteractedByTapRecorder(views::View* target_view) {
+  target_view->AddPreTargetHandler(this);
+}
+
+void InteractedByTapRecorder::OnEvent(ui::Event* event) {
+  if (event->type() == ui::ET_GESTURE_TAP) {
+    UMA_HISTOGRAM_ENUMERATION("ChromeOS.SystemTray.Interaction",
+                              INTERACTION_TYPE_TAP, INTERACTION_TYPE_COUNT);
+  } else if (event->type() == ui::ET_MOUSE_PRESSED) {
+    UMA_HISTOGRAM_ENUMERATION("ChromeOS.SystemTray.Interaction",
+                              INTERACTION_TYPE_CLICK, INTERACTION_TYPE_COUNT);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/tray/interacted_by_tap_recorder.h b/ash/system/tray/interacted_by_tap_recorder.h
new file mode 100644
index 0000000..1fb4ec2
--- /dev/null
+++ b/ash/system/tray/interacted_by_tap_recorder.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 ASH_SYSTEM_TRAY_INTERACTED_BY_TAP_RECORDER_H_
+#define ASH_SYSTEM_TRAY_INTERACTED_BY_TAP_RECORDER_H_
+
+#include "ui/events/event_handler.h"
+
+namespace views {
+class View;
+}  // namespace views
+
+namespace ash {
+
+// An event handler that will be installed as system tray view PreTargetHandler
+// to record Interaction metrics.
+class InteractedByTapRecorder : public ui::EventHandler {
+ public:
+  InteractedByTapRecorder(views::View* target_view);
+  ~InteractedByTapRecorder() override = default;
+
+  // Type of interaction. This enum is used to back an UMA histogram and should
+  // be treated as append-only.
+  enum InteractionType {
+    INTERACTION_TYPE_TAP = 0,
+    INTERACTION_TYPE_CLICK,
+    INTERACTION_TYPE_COUNT
+  };
+
+ private:
+  // ui::EventHandler:
+  void OnEvent(ui::Event* event) override;
+
+  DISALLOW_COPY_AND_ASSIGN(InteractedByTapRecorder);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_TRAY_INTERACTED_BY_TAP_RECORDER_H_
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index ad1a282..139ff5e 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -32,6 +32,7 @@
 #include "ash/system/ime/tray_ime_chromeos.h"
 #include "ash/system/keyboard_brightness/tray_keyboard_brightness.h"
 #include "ash/system/media_security/multi_profile_media_tray_item.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/tray_network.h"
@@ -58,7 +59,6 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/update/tray_update.h"
 #include "ash/system/user/tray_user.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/wm/container_finder.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/widget_finder.h"
@@ -236,17 +236,16 @@
     item->OnTrayViewDestroyed();
 }
 
-void SystemTray::InitializeTrayItems(
-    WebNotificationTray* web_notification_tray) {
-  DCHECK(web_notification_tray || features::IsSystemTrayUnifiedEnabled());
-  web_notification_tray_ = web_notification_tray;
+void SystemTray::InitializeTrayItems(NotificationTray* notification_tray) {
+  DCHECK(notification_tray || features::IsSystemTrayUnifiedEnabled());
+  notification_tray_ = notification_tray;
   TrayBackgroundView::Initialize();
   CreateItems();
 }
 
 void SystemTray::Shutdown() {
-  DCHECK(web_notification_tray_ || features::IsSystemTrayUnifiedEnabled());
-  web_notification_tray_ = nullptr;
+  DCHECK(notification_tray_ || features::IsSystemTrayUnifiedEnabled());
+  notification_tray_ = nullptr;
 }
 
 void SystemTray::CreateItems() {
@@ -564,8 +563,8 @@
     height =
         std::max(0, work_area.bottom() - bubble_view->GetBoundsInScreen().y());
   }
-  if (web_notification_tray_)
-    web_notification_tray_->SetTrayBubbleHeight(height);
+  if (notification_tray_)
+    notification_tray_->SetTrayBubbleHeight(height);
 }
 
 base::string16 SystemTray::GetAccessibleTimeString(
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index 8d0db51..d545436e 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -22,6 +22,7 @@
 namespace ash {
 
 enum class LoginStatus;
+class NotificationTray;
 class ScreenTrayItem;
 class SystemBubbleWrapper;
 class SystemTrayItem;
@@ -42,7 +43,6 @@
 class TrayTracing;
 class TrayUpdate;
 class TrayVPN;
-class WebNotificationTray;
 
 // There are different methods for creating bubble views.
 enum BubbleCreationType {
@@ -66,7 +66,7 @@
 
   // Calls TrayBackgroundView::Initialize(), creates the tray items, and
   // adds them to SystemTrayNotifier.
-  void InitializeTrayItems(WebNotificationTray* web_notification_tray);
+  void InitializeTrayItems(NotificationTray* notification_tray);
 
   // Resets internal pointers. This has to be called before deletion.
   void Shutdown();
@@ -210,7 +210,7 @@
   void RecordSystemMenuMetrics();
 
   // The web notification tray view that appears adjacent to this view.
-  WebNotificationTray* web_notification_tray_ = nullptr;
+  NotificationTray* notification_tray_ = nullptr;
 
   // Items.
   std::vector<std::unique_ptr<SystemTrayItem>> items_;
diff --git a/ash/system/tray/system_tray_unittest.cc b/ash/system/tray/system_tray_unittest.cc
index a3154eec..1ffb2be6 100644
--- a/ash/system/tray/system_tray_unittest.cc
+++ b/ash/system/tray/system_tray_unittest.cc
@@ -18,6 +18,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/tray/system_tray_bubble.h"
@@ -25,7 +26,6 @@
 #include "ash/system/tray/test_system_tray_item.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray_drag_controller.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/ash_test_views_delegate.h"
@@ -969,9 +969,8 @@
 
 TEST_F(SystemTrayTest, SystemTrayHeightWithBubble) {
   SystemTray* tray = GetPrimarySystemTray();
-  WebNotificationTray* notification_tray =
-      StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-          ->web_notification_tray();
+  NotificationTray* notification_tray =
+      StatusAreaWidgetTestHelper::GetStatusAreaWidget()->notification_tray();
 
   // Ensure the initial tray bubble height is zero.
   EXPECT_EQ(0, notification_tray->tray_bubble_height_for_test());
diff --git a/ash/system/tray/system_tray_view.cc b/ash/system/tray/system_tray_view.cc
index b9b34c0..2cc50fe 100644
--- a/ash/system/tray/system_tray_view.cc
+++ b/ash/system/tray/system_tray_view.cc
@@ -5,6 +5,7 @@
 #include "ash/system/tray/system_tray_view.h"
 
 #include "ash/shell.h"
+#include "ash/system/tray/interacted_by_tap_recorder.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "base/metrics/histogram_macros.h"
@@ -52,6 +53,8 @@
                                const std::vector<ash::SystemTrayItem*>& items)
     : time_to_click_recorder_(
           std::make_unique<TimeToClickRecorder>(system_tray, this)),
+      interacted_by_tap_recorder_(
+          std::make_unique<InteractedByTapRecorder>(this)),
       items_(items),
       system_tray_type_(system_tray_type) {
   SetLayoutManager(std::make_unique<BottomAlignedBoxLayout>());
diff --git a/ash/system/tray/system_tray_view.h b/ash/system/tray/system_tray_view.h
index bc5691d..d02b2e3b 100644
--- a/ash/system/tray/system_tray_view.h
+++ b/ash/system/tray/system_tray_view.h
@@ -43,6 +43,7 @@
   std::map<SystemTrayItem::UmaType, views::View*> tray_item_view_map_;
 
   std::unique_ptr<ui::EventHandler> time_to_click_recorder_;
+  std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
 
   std::vector<ash::SystemTrayItem*> items_;
 
diff --git a/ash/system/tray_caps_lock_unittest.cc b/ash/system/tray_caps_lock_unittest.cc
index 6a04ccd..ad06b86b 100644
--- a/ash/system/tray_caps_lock_unittest.cc
+++ b/ash/system/tray_caps_lock_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "ash/system/tray_caps_lock.h"
 
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_test_api.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 
 namespace ash {
@@ -18,11 +18,11 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
   }
 
   void TearDown() override {
-    WebNotificationTray::DisableAnimationsForTest(false);
+    NotificationTray::DisableAnimationsForTest(false);
     AshTestBase::TearDown();
   }
 
diff --git a/ash/system/tray_drag_controller.h b/ash/system/tray_drag_controller.h
index 6fd32ab..d96d88f8 100644
--- a/ash/system/tray_drag_controller.h
+++ b/ash/system/tray_drag_controller.h
@@ -15,7 +15,7 @@
 // The TrayDragController helps to process the swiping events that happened on
 // TrayBackgroundView or TrayBubbleView. Not all the TrayBackgroundView can be
 // dragged currently. Only ImeMenuTray, SystemTray, PaletteTray,
-// WebNotificationTray and their associated tray bubbles can be dragged.
+// NotificationTray and their associated tray bubbles can be dragged.
 class ASH_EXPORT TrayDragController {
  public:
   // The threshold of the velocity of the fling event.
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index e4e70df..9ce5cb1 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -6,13 +6,13 @@
 
 #include "ash/shell.h"
 #include "ash/system/date/date_view.h"
+#include "ash/system/message_center/ash_popup_alignment_delegate.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_model.h"
-#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/message_center/message_center.h"
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 1072f32..0448390 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -5,6 +5,7 @@
 #include "ash/system/unified/unified_system_tray_view.h"
 
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/system/tray/interacted_by_tap_recorder.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/feature_pods_container_view.h"
@@ -106,7 +107,9 @@
       sliders_container_(new UnifiedSlidersContainerView(initially_expanded)),
       system_info_view_(new UnifiedSystemInfoView()),
       system_tray_container_(new views::View()),
-      detailed_view_container_(new DetailedViewContainer()) {
+      detailed_view_container_(new DetailedViewContainer()),
+      interacted_by_tap_recorder_(
+          std::make_unique<InteractedByTapRecorder>(this)) {
   DCHECK(controller_);
 
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index e6a4a1e..c91462c5 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -81,6 +81,8 @@
   views::View* system_tray_container_;
   views::View* detailed_view_container_;
 
+  const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
+
   DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayView);
 };
 
diff --git a/ash/test/ash_test_suite.cc b/ash/test/ash_test_suite.cc
index 4c17af8..9e97c1a6 100644
--- a/ash/test/ash_test_suite.cc
+++ b/ash/test/ash_test_suite.cc
@@ -60,14 +60,12 @@
         ash_test_resources_200, ui::SCALE_FACTOR_200P);
   }
 
-  const bool is_mus = features::IsMusEnabled();
   const bool is_mash = base::FeatureList::IsEnabled(features::kMash);
-  AshTestHelper::config_ =
-      is_mash ? Config::MASH : is_mus ? Config::MUS : Config::CLASSIC;
+  AshTestHelper::config_ = is_mash ? Config::MASH : Config::CLASSIC;
 
   base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
-  env_ = aura::Env::CreateInstance(is_mus ? aura::Env::Mode::MUS
-                                          : aura::Env::Mode::LOCAL);
+  env_ = aura::Env::CreateInstance(is_mash ? aura::Env::Mode::MUS
+                                           : aura::Env::Mode::LOCAL);
 
   if (is_mash) {
     context_factory_ = std::make_unique<aura::test::AuraTestContextFactory>();
diff --git a/ash/window_manager.cc b/ash/window_manager.cc
index 4d03d5b..debfda7 100644
--- a/ash/window_manager.cc
+++ b/ash/window_manager.cc
@@ -95,6 +95,8 @@
       aura::client::kDrawAttentionKey,
       ui::mojom::WindowManager::kDrawAttention_Property,
       aura::PropertyConverter::CreateAcceptAnyValueCallback());
+  property_converter_->RegisterImageSkiaProperty(
+      kFrameImageActiveKey, ash::mojom::kFrameImageActive_Property);
   property_converter_->RegisterPrimitiveProperty(
       kPanelAttachedKey, ui::mojom::WindowManager::kPanelAttached_Property,
       aura::PropertyConverter::CreateAcceptAnyValueCallback());
@@ -128,6 +130,10 @@
       kRestoreWindowStateTypeOverrideKey,
       ash::mojom::kRestoreWindowStateTypeOverride_Property,
       base::BindRepeating(&ash::IsValidWindowStateType));
+  property_converter_->RegisterPrimitiveProperty(
+      kWindowTitleShownKey,
+      ui::mojom::WindowManager::kWindowTitleShown_Property,
+      aura::PropertyConverter::CreateAcceptAnyValueCallback());
 }
 
 WindowManager::~WindowManager() {
diff --git a/ash/wm/non_client_frame_controller.cc b/ash/wm/non_client_frame_controller.cc
index 308e183..604faa1a3 100644
--- a/ash/wm/non_client_frame_controller.cc
+++ b/ash/wm/non_client_frame_controller.cc
@@ -15,6 +15,7 @@
 #include "ash/frame/detached_title_area_renderer.h"
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/window_manager.h"
 #include "ash/wm/move_event_handler.h"
 #include "ash/wm/panels/panel_frame_view.h"
@@ -401,9 +402,7 @@
 }
 
 bool NonClientFrameController::ShouldShowWindowTitle() const {
-  // Only draw the title if the client hasn't declared any additional client
-  // areas which might conflict with it.
-  return window_ && additional_client_areas_.empty();
+  return window_ && window_->GetProperty(kWindowTitleShownKey);
 }
 
 views::ClientView* NonClientFrameController::CreateClientView(
diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc
index 018d222..fa11e4a60 100644
--- a/ash/wm/panels/panel_frame_view.cc
+++ b/ash/wm/panels/panel_frame_view.cc
@@ -40,7 +40,7 @@
                                     SkColor inactive_frame_color) {
   frame_header_->SetFrameColors(active_frame_color, inactive_frame_color);
   GetWidgetWindow()->SetProperty(aura::client::kTopViewColor,
-                                 frame_header_->GetInactiveFrameColor());
+                                 inactive_frame_color);
 }
 
 const char* PanelFrameView::GetClassName() const {
@@ -54,12 +54,12 @@
   frame_header_ = std::make_unique<DefaultFrameHeader>(
       frame_, this, caption_button_container_);
   GetWidgetWindow()->SetProperty(aura::client::kTopViewColor,
-                                 frame_header_->GetInactiveFrameColor());
+                                 DefaultFrameHeader::GetDefaultFrameColor());
 
   if (frame_->widget_delegate()->ShouldShowWindowIcon()) {
     window_icon_ = new views::ImageView();
     AddChildView(window_icon_);
-    frame_header_->set_left_header_view(window_icon_);
+    frame_header_->SetLeftHeaderView(window_icon_);
   }
 }
 
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 769cf68..8bca5ed 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -17,7 +17,7 @@
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
-#include "ash/system/web_notification/web_notification_tray.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_state.h"
@@ -71,13 +71,13 @@
         new ShelfViewTestAPI(GetPrimaryShelf()->GetShelfViewForTesting()));
     shelf_view_test_->SetAnimationDuration(1);
 
-    WebNotificationTray::DisableAnimationsForTest(true);
+    NotificationTray::DisableAnimationsForTest(true);
   }
 
   void TearDown() override {
     AshTestBase::TearDown();
 
-    WebNotificationTray::DisableAnimationsForTest(false);  // Reenable animation
+    NotificationTray::DisableAnimationsForTest(false);  // Reenable animation
   }
 
   aura::Window* CreateNormalWindow(const gfx::Rect& bounds) {
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc
index 21026e3..1207495b 100644
--- a/ash/wm/splitview/split_view_drag_indicators.cc
+++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -12,12 +12,12 @@
 #include "ash/wm/root_window_finder.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_controller.h"
+#include "ash/wm/splitview/split_view_highlight_view.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "base/i18n/rtl.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display_observer.h"
 #include "ui/views/controls/label.h"
@@ -30,14 +30,14 @@
 
 namespace {
 
-// The amount of round applied to the corners of the highlight views.
-constexpr int kHighlightScreenRoundRectRadiusDp = 4;
-
 // When animating, this is the location of the split view label as a ratio of
 // the width or height.
 constexpr double kSplitviewLabelExpandTranslationPrimaryAxisRatio = 0.20;
 constexpr double kSplitviewLabelShrinkTranslationPrimaryAxisRatio = 0.05;
 
+// When a preview is shown, the opposite highlight will shrink to this length.
+constexpr int kOtherHighlightLengthDp = 20;
+
 // Creates the widget responsible for displaying the indicators.
 std::unique_ptr<views::Widget> CreateWidget() {
   auto widget = std::make_unique<views::Widget>();
@@ -185,14 +185,13 @@
 // window has entered a snap region to display the bounds of the window, if it
 // were to get snapped.
 class SplitViewDragIndicators::SplitViewDragIndicatorsView
-    : public views::View,
-      public ui::ImplicitAnimationObserver {
+    : public views::View {
  public:
   SplitViewDragIndicatorsView() {
     left_highlight_view_ =
-        new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
+        new SplitViewHighlightView(/*is_right_or_bottom=*/false);
     right_highlight_view_ =
-        new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
+        new SplitViewHighlightView(/*is_right_or_bottom=*/true);
 
     left_highlight_view_->SetPaintToLayer();
     right_highlight_view_->SetPaintToLayer();
@@ -258,16 +257,16 @@
           DoSplitviewOpacityAnimation(view->layer(), animation_type);
         }
 
-        for (RoundedRectView* view : GetHighlightViews()) {
-          view->SetBackgroundColor(
-              indicator_state == IndicatorState::kCannotSnap ? SK_ColorBLACK
-                                                             : SK_ColorWHITE);
+        for (SplitViewHighlightView* view : GetHighlightViews()) {
+          view->SetColor(indicator_state == IndicatorState::kCannotSnap
+                             ? SK_ColorBLACK
+                             : SK_ColorWHITE);
           DoSplitviewOpacityAnimation(
               view->layer(), show ? SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN
                                   : SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT);
         }
 
-        Layout();
+        Layout(previous_indicator_state_ != IndicatorState::kNone);
         return;
       }
       case IndicatorState::kPreviewAreaLeft:
@@ -290,7 +289,7 @@
           DoSplitviewOpacityAnimation(right_highlight_view_->layer(),
                                       SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN);
         }
-        Layout();
+        Layout(/*animate=*/true);
         return;
       }
     }
@@ -315,7 +314,13 @@
   }
 
   // views::View:
-  void Layout() override {
+  void Layout() override { Layout(/*animate=*/false); }
+
+ private:
+  // Layout the bounds of the highlight views and helper labels. One should
+  // animate when changing states, but not when bounds or orientation is
+  // changed.
+  void Layout(bool animate) {
     const bool landscape = Shell::Get()
                                ->split_view_controller()
                                ->IsCurrentScreenOrientationLandscape();
@@ -337,17 +342,17 @@
             ? kHighlightScreenEdgePaddingDp
             : height() - highlight_height - kHighlightScreenEdgePaddingDp);
 
-    // Apply a transform to the left and right highlights if one is to expand to
-    // show a preview area. The expanding window will be transformed by
-    // |main_transform|. The other highlight, which will shrink and fade out,
-    // will be transformed by |other_transform|.
-    gfx::Transform main_transform, other_transform;
+    gfx::Rect left_highlight_bounds, right_highlight_bounds;
+    left_highlight_bounds =
+        gfx::Rect(kHighlightScreenEdgePaddingDp, kHighlightScreenEdgePaddingDp,
+                  highlight_width, highlight_height);
+    right_highlight_bounds =
+        gfx::Rect(right_bottom_origin.x(), right_bottom_origin.y(),
+                  highlight_width, highlight_height);
+
     const bool preview_left =
-        IsPreviewAreaOnLeftTopOfScreen(indicator_state_) ||
-        IsPreviewAreaOnLeftTopOfScreen(previous_indicator_state_);
-    if (IsPreviewAreaState(indicator_state_) ||
-        (indicator_state_ == IndicatorState::kDragArea &&
-         IsPreviewAreaState(previous_indicator_state_))) {
+        (indicator_state_ == IndicatorState::kPreviewAreaLeft);
+    if (IsPreviewAreaState(indicator_state_)) {
       // Get the preview area bounds from the split view controller.
       gfx::Rect preview_area_bounds =
           Shell::Get()->split_view_controller()->GetSnappedWindowBoundsInScreen(
@@ -357,91 +362,41 @@
       preview_area_bounds.Inset(kHighlightScreenEdgePaddingDp,
                                 kHighlightScreenEdgePaddingDp);
 
-      // Compute both |main_transform| and |other_transform|. In landscape mode
-      // use x and width values. In portrait mode use y and height values.
+      // Calculate the bounds of the other highlight, which is the one that
+      // shrinks and fades away, while the other one, the preview area, expands
+      // and takes up half the screen.
+      gfx::Rect other_bounds;
       if (landscape) {
-        if (!preview_left) {
-          // |main_transform| corresponds to the right window, which changes x
-          // position as well as scale, so apply a translation.
-          main_transform.Translate(gfx::Vector2dF(
-              -(right_bottom_origin.x() - width() +
-                kHighlightScreenEdgePaddingDp + preview_area_bounds.width()),
-              0.f));
-        }
-        // Apply a scale to scale the width to the width of
-        // |preview_area_bounds|.
-        main_transform.Scale(static_cast<double>(preview_area_bounds.width()) /
-                                 static_cast<double>(highlight_width),
-                             1.0);
-
-        if (preview_left) {
-          // |other_transform| corresponds to the right window, which changes x
-          // position as well as scale, so apply a translation.
-          other_transform.Translate(gfx::Vector2dF(highlight_width, 0.f));
-        }
-        // Scale the other window so that it becomes hidden.
-        other_transform.Scale(1.0 / static_cast<double>(highlight_width), 1.0);
+        other_bounds.set_size(
+            gfx::Size(kOtherHighlightLengthDp,
+                      height() - 2 * kHighlightScreenEdgePaddingDp));
+        other_bounds.set_origin(gfx::Point(
+            width() - kOtherHighlightLengthDp - kHighlightScreenEdgePaddingDp,
+            kHighlightScreenEdgePaddingDp));
       } else {
-        if (!preview_left) {
-          // |main_transform| corresponds to the bottom window, which changes y
-          // position as well as scale, so apply a translation.
-          main_transform.Translate(gfx::Vector2dF(
-              0.f,
-              -(right_bottom_origin.y() - height() +
-                kHighlightScreenEdgePaddingDp + preview_area_bounds.height())));
-        }
-        // Apply a scale to scale the height to the height of
-        // |preview_area_bounds|.
-        main_transform.Scale(1.0,
-                             static_cast<double>(preview_area_bounds.height()) /
-                                 static_cast<double>(highlight_height));
-
-        if (preview_left) {
-          // |other_transform| corresponds to the bottom window, which changes y
-          // position as well as scale, so apply a translation.
-          other_transform.Translate(gfx::Vector2dF(0.f, highlight_height));
-        }
-        // Scale the other window so that it becomes hidden.
-        other_transform.Scale(1.0, 1.0 / static_cast<double>(highlight_height));
+        other_bounds.set_size(
+            gfx::Size(width() - 2 * kHighlightScreenEdgePaddingDp,
+                      kOtherHighlightLengthDp));
+        other_bounds.set_origin(gfx::Point(kHighlightScreenEdgePaddingDp,
+                                           height() - kOtherHighlightLengthDp -
+                                               kHighlightScreenEdgePaddingDp));
       }
 
-      if (IsPreviewAreaState(previous_indicator_state_)) {
-        // If the previous state was a preview state, first apply a transform,
-        // otherwise no animation will happen if we try to transform from
-        // identity to identity. (OnImplicitAnimationsCompleted sets the bounds
-        // and all transforms to identity to preserve rounded edges).
-        left_highlight_view_->layer()->SetTransform(
-            preview_left ? main_transform : other_transform);
-        right_highlight_view_->layer()->SetTransform(
-            preview_left ? other_transform : main_transform);
-        main_transform.MakeIdentity();
-        other_transform.MakeIdentity();
+      if (IsPreviewAreaOnLeftTopOfScreen(indicator_state_)) {
+        left_highlight_bounds = preview_area_bounds;
+        right_highlight_bounds = other_bounds;
+      } else {
+        other_bounds.set_origin(gfx::Point(kHighlightScreenEdgePaddingDp,
+                                           kHighlightScreenEdgePaddingDp));
+        left_highlight_bounds = other_bounds;
+        right_highlight_bounds = preview_area_bounds;
       }
     }
 
-    left_highlight_bounds_ =
-        gfx::Rect(kHighlightScreenEdgePaddingDp, kHighlightScreenEdgePaddingDp,
-                  highlight_width, highlight_height);
-    right_highlight_bounds_ =
-        gfx::Rect(right_bottom_origin.x(), right_bottom_origin.y(),
-                  highlight_width, highlight_height);
-    if (base::i18n::IsRTL() && landscape) {
-      gfx::Rect temp = left_highlight_bounds_;
-      left_highlight_bounds_ = right_highlight_bounds_;
-      right_highlight_bounds_ = temp;
-    }
-
-    DoSplitviewTransformAnimation(
-        left_highlight_view_->layer(),
-        SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT,
-        preview_left ? main_transform : other_transform, this);
-    left_highlight_view_->SetBoundsRect(left_highlight_bounds_);
-
-    DoSplitviewTransformAnimation(
-        right_highlight_view_->layer(),
-        SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT,
-        preview_left ? other_transform : main_transform, nullptr);
-    right_highlight_view_->SetBoundsRect(right_highlight_bounds_);
+    left_highlight_view_->SetBounds(GetMirroredRect(left_highlight_bounds),
+                                    landscape, animate);
+    right_highlight_view_->SetBounds(GetMirroredRect(right_highlight_bounds),
+                                     landscape, animate);
 
     // Calculate the bounds of the views which contain the guidance text and
     // icon. Rotate the two views in landscape mode.
@@ -493,57 +448,18 @@
         nullptr);
   }
 
-  // ui::ImplicitAnimationObserver:
-  void OnImplicitAnimationsCompleted() override {
-    // Set the final bounds and the layer transforms to identity, so that the
-    // proper rounding on the rounded rect corners show up.
-    const bool flip =
-        base::i18n::IsRTL() && Shell::Get()
-                                   ->split_view_controller()
-                                   ->IsCurrentScreenOrientationLandscape();
-    const bool left =
-        (indicator_state_ == IndicatorState::kPreviewAreaLeft && !flip) ||
-        (indicator_state_ == IndicatorState::kPreviewAreaRight && flip);
-    gfx::Rect preview_area_bounds =
-        Shell::Get()->split_view_controller()->GetSnappedWindowBoundsInScreen(
-            GetWidget()->GetNativeWindow(),
-            left ? SplitViewController::LEFT : SplitViewController::RIGHT);
-    preview_area_bounds.Inset(kHighlightScreenEdgePaddingDp,
-                              kHighlightScreenEdgePaddingDp);
-
-    constexpr gfx::Rect kEmptyRect;
-    left_highlight_view_->layer()->SetTransform(gfx::Transform());
-    right_highlight_view_->layer()->SetTransform(gfx::Transform());
-    if (IsPreviewAreaOnLeftTopOfScreen(indicator_state_)) {
-      left_highlight_view_->SetBoundsRect(preview_area_bounds);
-      right_highlight_view_->SetBoundsRect(kEmptyRect);
-    } else if (IsPreviewAreaState(indicator_state_)) {
-      left_highlight_view_->SetBoundsRect(kEmptyRect);
-      right_highlight_view_->SetBoundsRect(preview_area_bounds);
-    } else {
-      left_highlight_view_->SetBoundsRect(left_highlight_bounds_);
-      right_highlight_view_->SetBoundsRect(right_highlight_bounds_);
-    }
-  }
-
- private:
-  std::vector<RoundedRectView*> GetHighlightViews() {
+  std::vector<SplitViewHighlightView*> GetHighlightViews() {
     return {left_highlight_view_, right_highlight_view_};
   }
   std::vector<RotatedImageLabelView*> GetTextViews() {
     return {left_rotated_view_, right_rotated_view_};
   }
 
-  RoundedRectView* left_highlight_view_ = nullptr;
-  RoundedRectView* right_highlight_view_ = nullptr;
+  SplitViewHighlightView* left_highlight_view_ = nullptr;
+  SplitViewHighlightView* right_highlight_view_ = nullptr;
   RotatedImageLabelView* left_rotated_view_ = nullptr;
   RotatedImageLabelView* right_rotated_view_ = nullptr;
 
-  // Cache the bounds calculated in Layout(), so that they do not have to be
-  // recalculated when animation is finished.
-  gfx::Rect left_highlight_bounds_;
-  gfx::Rect right_highlight_bounds_;
-
   IndicatorState indicator_state_ = IndicatorState::kNone;
   IndicatorState previous_indicator_state_ = IndicatorState::kNone;
 
diff --git a/ash/wm/splitview/split_view_highlight_view.cc b/ash/wm/splitview/split_view_highlight_view.cc
new file mode 100644
index 0000000..f5cb8ca
--- /dev/null
+++ b/ash/wm/splitview/split_view_highlight_view.cc
@@ -0,0 +1,174 @@
+// 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/wm/splitview/split_view_highlight_view.h"
+
+#include "ash/wm/overview/rounded_rect_view.h"
+#include "ash/wm/splitview/split_view_utils.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+namespace {
+
+// The amount of round applied to the corners of the highlight views.
+constexpr int kHighlightScreenRoundRectRadiusDp = 4;
+
+constexpr int kRoundRectPaddingDp = 10;
+
+gfx::Transform CalculateTransformFromRects(const gfx::Rect& src,
+                                           const gfx::Rect& dst,
+                                           bool landscape) {
+  // In portrait, rtl will have no effect on this view.
+  const bool is_rtl = base::i18n::IsRTL() && landscape;
+  const bool should_scale =
+      src.width() != dst.width() || src.height() != dst.height();
+
+  // Add a translatation. In rtl, translate in the opposite direction to account
+  // for the flip.
+  gfx::Transform transform;
+  transform.Translate(is_rtl && !should_scale ? src.origin() - dst.origin()
+                                              : dst.origin() - src.origin());
+  if (should_scale) {
+    // In rtl a extra translation needs to be added to account for the flipped
+    // scaling.
+    if (is_rtl) {
+      int x_translation = 0;
+      if ((src.x() > dst.x() && src.width() < dst.width()) ||
+          (src.x() == dst.x() && src.width() > dst.width())) {
+        x_translation = std::abs(dst.width() - src.width());
+      } else {
+        x_translation = -std::abs(dst.width() - src.width());
+      }
+      transform.Translate(gfx::Vector2d(x_translation, 0));
+    }
+    transform.Scale(
+        static_cast<float>(dst.width()) / static_cast<float>(src.width()),
+        static_cast<float>(dst.height()) / static_cast<float>(src.height()));
+  }
+  return transform;
+}
+
+}  // namespace
+
+SplitViewHighlightView::SplitViewHighlightView(bool is_right_or_bottom)
+    : is_right_or_bottom_(is_right_or_bottom) {
+  left_top_ =
+      new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
+  right_bottom_ =
+      new RoundedRectView(kHighlightScreenRoundRectRadiusDp, SK_ColorWHITE);
+
+  left_top_->SetPaintToLayer();
+  left_top_->layer()->SetFillsBoundsOpaquely(false);
+  right_bottom_->SetPaintToLayer();
+  right_bottom_->layer()->SetFillsBoundsOpaquely(false);
+
+  middle_ = new views::View();
+  middle_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
+  middle_->layer()->SetColor(SK_ColorWHITE);
+
+  AddChildView(left_top_);
+  AddChildView(right_bottom_);
+  AddChildView(middle_);
+}
+
+SplitViewHighlightView::~SplitViewHighlightView() = default;
+
+void SplitViewHighlightView::SetBounds(const gfx::Rect& bounds,
+                                       bool landscape,
+                                       bool animate) {
+  if (bounds == this->bounds() && landscape == landscape_)
+    return;
+
+  landscape_ = landscape;
+
+  const gfx::Rect old_bounds = this->bounds();
+  const gfx::Vector2d offset = old_bounds.origin() - bounds.origin();
+  SetBoundsRect(bounds);
+  // Shift the bounds of the right bottom view if needed before applying the
+  // transform.
+  const bool slides_from_right = base::i18n::IsRTL() && landscape
+                                     ? !is_right_or_bottom_
+                                     : is_right_or_bottom_;
+  if (slides_from_right && animate && !offset.IsZero()) {
+    gfx::Rect old_left_top_bounds = left_top_->bounds();
+    gfx::Rect old_right_middle_bounds = right_bottom_->bounds();
+    gfx::Rect old_middle_bounds = middle_->bounds();
+
+    old_left_top_bounds.Offset(offset);
+    old_right_middle_bounds.Offset(offset);
+    old_middle_bounds.Offset(offset);
+
+    left_top_->SetBoundsRect(old_left_top_bounds);
+    right_bottom_->SetBoundsRect(old_right_middle_bounds);
+    middle_->SetBoundsRect(old_middle_bounds);
+  }
+
+  // Calculate the new bounds. The middle should take as much space as possible,
+  // and the other two should take just enough space so they can display rounded
+  // corners.
+  gfx::Rect left_top_bounds, right_bottom_bounds;
+  gfx::Rect middle_bounds = bounds;
+
+  // The thickness of the two outer views should be the amount of rounding, plus
+  // a little padding. There will be some overlap to simply the code (we use a
+  // rectangle that is rounded on all sides, but cover half the sides instead of
+  // creating a new class that is only rounded on half the sides).
+  const int thickness = kHighlightScreenRoundRectRadiusDp + kRoundRectPaddingDp;
+  if (landscape) {
+    left_top_bounds = gfx::Rect(0, 0, thickness, bounds.height());
+    right_bottom_bounds = left_top_bounds;
+    right_bottom_bounds.Offset(bounds.width() - thickness, 0);
+    middle_bounds.Offset(-bounds.x(), -bounds.y());
+    middle_bounds.Inset(kHighlightScreenRoundRectRadiusDp, 0);
+  } else {
+    left_top_bounds = gfx::Rect(0, 0, bounds.width(), thickness);
+    right_bottom_bounds = left_top_bounds;
+    right_bottom_bounds.Offset(0, bounds.height() - thickness);
+    middle_bounds.Offset(-bounds.x(), -bounds.y());
+    middle_bounds.Inset(0, kHighlightScreenRoundRectRadiusDp);
+  }
+
+  left_top_bounds = GetMirroredRect(left_top_bounds);
+  right_bottom_bounds = GetMirroredRect(right_bottom_bounds);
+  middle_bounds = GetMirroredRect(middle_bounds);
+
+  // If |animate|, calculate the needed transform from old bounds to new bounds
+  // and apply it. Otherwise set the new bounds and reset the transforms on all
+  // items.
+  if (animate) {
+    DoSplitviewTransformAnimation(
+        middle_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT,
+        CalculateTransformFromRects(middle_->bounds(), middle_bounds,
+                                    landscape),
+        nullptr);
+    DoSplitviewTransformAnimation(
+        left_top_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT,
+        CalculateTransformFromRects(left_top_->bounds(), left_top_bounds,
+                                    landscape),
+        nullptr);
+    DoSplitviewTransformAnimation(
+        right_bottom_->layer(), SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN_OUT,
+        CalculateTransformFromRects(right_bottom_->bounds(),
+                                    right_bottom_bounds, landscape),
+        nullptr);
+  } else {
+    left_top_->layer()->SetTransform(gfx::Transform());
+    right_bottom_->layer()->SetTransform(gfx::Transform());
+    middle_->layer()->SetTransform(gfx::Transform());
+
+    left_top_->SetBoundsRect(left_top_bounds);
+    right_bottom_->SetBoundsRect(right_bottom_bounds);
+    middle_->SetBoundsRect(middle_bounds);
+  }
+}
+
+void SplitViewHighlightView::SetColor(SkColor color) {
+  left_top_->SetBackgroundColor(color);
+  right_bottom_->SetBackgroundColor(color);
+  middle_->layer()->SetColor(color);
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/splitview/split_view_highlight_view.h b/ash/wm/splitview/split_view_highlight_view.h
new file mode 100644
index 0000000..ea2dd55
--- /dev/null
+++ b/ash/wm/splitview/split_view_highlight_view.h
@@ -0,0 +1,53 @@
+// 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_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_H_
+#define ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+class RoundedRectView;
+class SplitViewHighlightViewTestApi;
+
+// View that is used for displaying and animating the highlights which tell
+// users where to drag windows to enter splitview, and previews the space which
+// a snapped window will occupy. It is a view consisting of a rectangle with
+// rounded corners on the left or top, a rectangle in the middle and a rectangle
+// with rounded corners on the right or bottom. It is done this way to ensure
+// rounded corners remain the same during the duration of an animation.
+// (Transforming a rounded rect will stretch the corners, and having to repaint
+// every animation tick is expensive.)
+class ASH_EXPORT SplitViewHighlightView : public views::View {
+ public:
+  explicit SplitViewHighlightView(bool is_right_or_bottom);
+  ~SplitViewHighlightView() override;
+
+  void SetBounds(const gfx::Rect& bounds, bool landscape, bool animate);
+
+  void SetColor(SkColor color);
+
+ private:
+  friend class SplitViewHighlightViewTestApi;
+
+  // The three components of this view.
+  RoundedRectView* left_top_ = nullptr;
+  RoundedRectView* right_bottom_ = nullptr;
+  views::View* middle_ = nullptr;
+
+  bool landscape_ = true;
+  // Determines whether this particular highlight view is located at the right
+  // or bottom of the screen. These highlights animate in the opposite direction
+  // as left or top highlights, so when we use SetBounds extra calucations have
+  // to be done to ensure the animation is correct.
+  const bool is_right_or_bottom_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightView);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_H_
\ No newline at end of file
diff --git a/ash/wm/splitview/split_view_highlight_view_test_api.cc b/ash/wm/splitview/split_view_highlight_view_test_api.cc
new file mode 100644
index 0000000..e3cc60e0
--- /dev/null
+++ b/ash/wm/splitview/split_view_highlight_view_test_api.cc
@@ -0,0 +1,30 @@
+// 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/wm/splitview/split_view_highlight_view_test_api.h"
+
+#include "ash/wm/overview/rounded_rect_view.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+SplitViewHighlightViewTestApi::SplitViewHighlightViewTestApi(
+    SplitViewHighlightView* highlight_view)
+    : highlight_view_(highlight_view) {}
+
+SplitViewHighlightViewTestApi::~SplitViewHighlightViewTestApi() = default;
+
+views::View* SplitViewHighlightViewTestApi::GetLeftTopView() {
+  return static_cast<views::View*>(highlight_view_->left_top_);
+}
+
+views::View* SplitViewHighlightViewTestApi::GetRightBottomView() {
+  return static_cast<views::View*>(highlight_view_->right_bottom_);
+}
+
+views::View* SplitViewHighlightViewTestApi::GetMiddleView() {
+  return highlight_view_->middle_;
+}
+
+}  // namespace ash
diff --git a/ash/wm/splitview/split_view_highlight_view_test_api.h b/ash/wm/splitview/split_view_highlight_view_test_api.h
new file mode 100644
index 0000000..2c3e09c
--- /dev/null
+++ b/ash/wm/splitview/split_view_highlight_view_test_api.h
@@ -0,0 +1,36 @@
+// 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_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
+#define ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
+
+#include "ash/wm/splitview/split_view_highlight_view.h"
+#include "base/macros.h"
+
+namespace views {
+class View;
+}  // namespace views
+
+namespace ash {
+
+// Use the api in this class to test SplitViewHighlightView.
+class SplitViewHighlightViewTestApi {
+ public:
+  explicit SplitViewHighlightViewTestApi(
+      SplitViewHighlightView* highlight_view);
+  ~SplitViewHighlightViewTestApi();
+
+  views::View* GetLeftTopView();
+  views::View* GetRightBottomView();
+  views::View* GetMiddleView();
+
+ private:
+  SplitViewHighlightView* highlight_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewTestApi);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_SPLITVIEW_SPLIT_VIEW_HIGHLIGHT_VIEW_TEST_API_H_
\ No newline at end of file
diff --git a/ash/wm/splitview/split_view_highlight_view_unittest.cc b/ash/wm/splitview/split_view_highlight_view_unittest.cc
new file mode 100644
index 0000000..6a44b17
--- /dev/null
+++ b/ash/wm/splitview/split_view_highlight_view_unittest.cc
@@ -0,0 +1,199 @@
+// 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/wm/splitview/split_view_highlight_view.h"
+
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/splitview/split_view_highlight_view_test_api.h"
+#include "base/test/icu_test_util.h"
+#include "ui/gfx/transform.h"
+
+namespace ash {
+
+namespace {
+
+gfx::Transform GetTransform(views::View* view) {
+  DCHECK(view && view->layer());
+  return view->layer()->transform();
+}
+
+}  // namespace
+
+class SplitViewHighlightViewTest : public AshTestBase {
+ public:
+  SplitViewHighlightViewTest() = default;
+  ~SplitViewHighlightViewTest() override = default;
+
+  SplitViewHighlightView* left_highlight() { return left_highlight_.get(); }
+  SplitViewHighlightView* right_highlight() { return right_highlight_.get(); }
+
+  // test::AshTestBase:
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    left_highlight_ = std::make_unique<SplitViewHighlightView>(false);
+    right_highlight_ = std::make_unique<SplitViewHighlightView>(true);
+  }
+
+ private:
+  std::unique_ptr<SplitViewHighlightView> left_highlight_;
+  std::unique_ptr<SplitViewHighlightView> right_highlight_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewTest);
+};
+
+// Tests setting and animating bounds for the split view highlight view in
+// landscape mode.
+TEST_F(SplitViewHighlightViewTest, LandscapeBounds) {
+  const gfx::Rect bounds(0, 0, 100, 100);
+  left_highlight()->SetBounds(bounds, /*landscape=*/true, /*animate=*/false);
+
+  // Tests that setting bounds without animations in landscape mode will set the
+  // bounds of the components correctly, without any transforms.
+  SplitViewHighlightViewTestApi test_api(left_highlight());
+  EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView()).IsIdentity());
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
+
+  // Tests that after animating to new bounds, the components have the same
+  // bounds, but have transforms.
+  const gfx::Rect new_bounds(0, 0, 200, 100);
+  left_highlight()->SetBounds(new_bounds, /*landscape=*/true, /*animate=*/true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
+  gfx::Transform expected_middle_transform;
+  expected_middle_transform.Scale(2.16, 1);
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
+                  .ApproximatelyEqual(expected_middle_transform));
+  gfx::Transform expected_end_transform;
+  expected_end_transform.Translate(100, 0);
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView())
+                  .ApproximatelyEqual(expected_end_transform));
+}
+
+// Tests setting and animating bounds for the split view highlight view in
+// landscape mode for rtl languages.
+TEST_F(SplitViewHighlightViewTest, LandscapeBoundsInRtl) {
+  base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
+
+  const gfx::Rect bounds(0, 0, 100, 100);
+  left_highlight()->SetBounds(bounds, /*landscape=*/true, /*animate=*/false);
+
+  // Tests that setting bounds without animations in landscape mode will set the
+  // bounds of the components correctly, without any transforms. In rtl, the
+  // bounds of the outer components are swapped.
+  SplitViewHighlightViewTestApi test_api(left_highlight());
+  EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetRightBottomView()->bounds());
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView()).IsIdentity());
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
+
+  // Tests that after animating to new bounds, the components have the same
+  // bounds, but have transforms. In rtl the beginning element is the one that
+  // is translated instead. The middle element has a extra translation in its
+  // transform to account for the flipped scaling.
+  const gfx::Rect new_bounds(0, 0, 200, 100);
+  left_highlight()->SetBounds(new_bounds, /*landscape=*/true, /*animate=*/true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetRightBottomView()->bounds());
+  gfx::Transform expected_begin_transform;
+  expected_begin_transform.Translate(-100, 0);
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView())
+                  .ApproximatelyEqual(expected_begin_transform));
+  gfx::Transform expected_middle_transform;
+  expected_middle_transform.Translate(-100, 0);
+  expected_middle_transform.Scale(2.16, 1);
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
+                  .ApproximatelyEqual(expected_middle_transform));
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
+}
+
+class SplitViewHighlightViewPortraitTest
+    : public SplitViewHighlightViewTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  SplitViewHighlightViewPortraitTest()
+      : scoped_locale_(GetParam() ? "he" : "") {}
+  ~SplitViewHighlightViewPortraitTest() override = default;
+
+ private:
+  // Restores locale to the default when destructor is called.
+  base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplitViewHighlightViewPortraitTest);
+};
+
+// Tests setting and animating bounds for the split view highlight view in
+// portrait mode. The bounds should remain the same in ltr or rtl.
+TEST_P(SplitViewHighlightViewPortraitTest, Bounds) {
+  const gfx::Rect bounds(0, 0, 100, 100);
+  left_highlight()->SetBounds(bounds, /*landscape=*/false, /*animate=*/false);
+
+  SplitViewHighlightViewTestApi test_api(left_highlight());
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 14), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 4, 100, 92), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 86, 100, 14), test_api.GetRightBottomView()->bounds());
+
+  const gfx::Rect new_bounds(0, 0, 100, 200);
+  left_highlight()->SetBounds(new_bounds, /*landscape=*/false,
+                              /*animate=*/true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 14), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 4, 100, 92), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(0, 86, 100, 14), test_api.GetRightBottomView()->bounds());
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView()).IsIdentity());
+  gfx::Transform expected_middle_transform;
+  expected_middle_transform.Scale(1, 2.16);
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
+                  .ApproximatelyEqual(expected_middle_transform));
+  gfx::Transform expected_end_transform;
+  expected_end_transform.Translate(0, 100);
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView())
+                  .ApproximatelyEqual(expected_end_transform));
+}
+
+INSTANTIATE_TEST_CASE_P(Bounds,
+                        SplitViewHighlightViewPortraitTest,
+                        testing::Bool());
+
+TEST_F(SplitViewHighlightViewTest, RightBounds) {
+  const gfx::Rect bounds(100, 0, 100, 100);
+  right_highlight()->SetBounds(bounds, /*landscape=*/true, /*animate=*/false);
+
+  SplitViewHighlightViewTestApi test_api(right_highlight());
+  EXPECT_EQ(gfx::Rect(0, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(4, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(86, 0, 14, 100), test_api.GetRightBottomView()->bounds());
+
+  const gfx::Rect new_bounds(0, 0, 200, 100);
+  right_highlight()->SetBounds(new_bounds, /*landscape=*/true,
+                               /*animate=*/true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(gfx::Rect(100, 0, 14, 100), test_api.GetLeftTopView()->bounds());
+  EXPECT_EQ(gfx::Rect(104, 0, 92, 100), test_api.GetMiddleView()->bounds());
+  EXPECT_EQ(gfx::Rect(186, 0, 14, 100),
+            test_api.GetRightBottomView()->bounds());
+  gfx::Transform expected_begin_transform;
+  expected_begin_transform.Translate(-100, 0);
+  EXPECT_TRUE(GetTransform(test_api.GetLeftTopView())
+                  .ApproximatelyEqual(expected_begin_transform));
+  gfx::Transform expected_middle_transform;
+  expected_middle_transform.Translate(-100, 0);
+  expected_middle_transform.Scale(2.16, 1);
+  EXPECT_TRUE(GetTransform(test_api.GetMiddleView())
+                  .ApproximatelyEqual(expected_middle_transform));
+  EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5f04e581..ee3ed563 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2120,6 +2120,7 @@
     sources = [
       "fuchsia/test.fidl",
     ]
+    namespace = "base"
   }
 }
 
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc
index 93e51df7..8554673 100644
--- a/base/allocator/partition_allocator/partition_alloc.cc
+++ b/base/allocator/partition_allocator/partition_alloc.cc
@@ -257,38 +257,46 @@
   return true;
 }
 
-void* PartitionRootGeneric::Realloc(void* ptr,
-                                    size_t new_size,
-                                    const char* type_name) {
+void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
+                                   int flags,
+                                   void* ptr,
+                                   size_t new_size,
+                                   const char* type_name) {
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
-  return realloc(ptr, new_size);
+  void* result = realloc(ptr, new_size);
+  CHECK(result || flags & PartitionAllocReturnNull);
+  return result;
 #else
   if (UNLIKELY(!ptr))
-    return this->Alloc(new_size, type_name);
+    return PartitionAllocGenericFlags(root, flags, new_size, type_name);
   if (UNLIKELY(!new_size)) {
-    this->Free(ptr);
+    root->Free(ptr);
     return nullptr;
   }
 
-  if (new_size > kGenericMaxDirectMapped)
-    internal::PartitionExcessiveAllocationSize();
+  if (new_size > kGenericMaxDirectMapped) {
+    if (flags & PartitionAllocReturnNull)
+      return nullptr;
+    else
+      internal::PartitionExcessiveAllocationSize();
+  }
 
   internal::PartitionPage* page = internal::PartitionPage::FromPointer(
       internal::PartitionCookieFreePointerAdjust(ptr));
   // TODO(palmer): See if we can afford to make this a CHECK.
-  DCHECK(IsValidPage(page));
+  DCHECK(root->IsValidPage(page));
 
   if (UNLIKELY(page->bucket->is_direct_mapped())) {
     // We may be able to perform the realloc in place by changing the
     // accessibility of memory pages and, if reducing the size, decommitting
     // them.
-    if (PartitionReallocDirectMappedInPlace(this, page, new_size)) {
+    if (PartitionReallocDirectMappedInPlace(root, page, new_size)) {
       PartitionAllocHooks::ReallocHookIfEnabled(ptr, ptr, new_size, type_name);
       return ptr;
     }
   }
 
-  size_t actual_new_size = this->ActualSize(new_size);
+  size_t actual_new_size = root->ActualSize(new_size);
   size_t actual_old_size = PartitionAllocGetSize(ptr);
 
   // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the
@@ -309,17 +317,30 @@
   }
 
   // This realloc cannot be resized in-place. Sadness.
-  void* ret = this->Alloc(new_size, type_name);
+  void* ret = PartitionAllocGenericFlags(root, flags, new_size, type_name);
+  if (!ret) {
+    if (flags & PartitionAllocReturnNull)
+      return nullptr;
+    else
+      internal::PartitionExcessiveAllocationSize();
+  }
+
   size_t copy_size = actual_old_size;
   if (new_size < copy_size)
     copy_size = new_size;
 
   memcpy(ret, ptr, copy_size);
-  this->Free(ptr);
+  root->Free(ptr);
   return ret;
 #endif
 }
 
+void* PartitionRootGeneric::Realloc(void* ptr,
+                                    size_t new_size,
+                                    const char* type_name) {
+  return PartitionReallocGenericFlags(this, 0, ptr, new_size, type_name);
+}
+
 static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
   const internal::PartitionBucket* bucket = page->bucket;
   size_t slot_size = bucket->slot_size;
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h
index 59b1c2d..79d09053 100644
--- a/base/allocator/partition_allocator/partition_alloc.h
+++ b/base/allocator/partition_allocator/partition_alloc.h
@@ -359,6 +359,12 @@
 #endif
 }
 
+BASE_EXPORT void* PartitionReallocGenericFlags(PartitionRootGeneric* root,
+                                               int flags,
+                                               void* ptr,
+                                               size_t new_size,
+                                               const char* type_name);
+
 ALWAYS_INLINE size_t PartitionRootGeneric::ActualSize(size_t size) {
 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
   return size;
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index f8081fc4..4bf6b26d 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -182,7 +182,7 @@
     }
   }
 
-  void DoReturnNullTest(size_t allocSize) {
+  void DoReturnNullTest(size_t allocSize, bool use_realloc) {
     // TODO(crbug.com/678782): Where necessary and possible, disable the
     // platform's OOM-killing behavior. OOM-killing makes this test flaky on
     // low-memory devices.
@@ -202,9 +202,17 @@
     int i;
 
     for (i = 0; i < numAllocations; ++i) {
-      ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
-                                           PartitionAllocReturnNull, allocSize,
-                                           type_name);
+      if (use_realloc) {
+        ptrs[i] = PartitionAllocGenericFlags(
+            generic_allocator.root(), PartitionAllocReturnNull, 1, type_name);
+        ptrs[i] = PartitionReallocGenericFlags(generic_allocator.root(),
+                                               PartitionAllocReturnNull,
+                                               ptrs[i], allocSize, type_name);
+      } else {
+        ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(),
+                                             PartitionAllocReturnNull,
+                                             allocSize, type_name);
+      }
       if (!i)
         EXPECT_TRUE(ptrs[0]);
       if (!ptrs[i]) {
@@ -1302,14 +1310,26 @@
 // sufficient unreserved virtual memory around for the later one(s).
 TEST_F(PartitionAllocReturnNullTest, RepeatedReturnNullDirect) {
   // A direct-mapped allocation size.
-  DoReturnNullTest(32 * 1024 * 1024);
+  DoReturnNullTest(32 * 1024 * 1024, false);
 }
 
 // Test "return null" with a 512 kB block size.
 TEST_F(PartitionAllocReturnNullTest, RepeatedReturnNull) {
   // A single-slot but non-direct-mapped allocation size.
-  DoReturnNullTest(512 * 1024);
+  DoReturnNullTest(512 * 1024, false);
 }
+
+// Repeating the above tests using Realloc instead of Alloc.
+class PartitionReallocReturnNullTest : public PartitionAllocTest {};
+
+TEST_F(PartitionReallocReturnNullTest, RepeatedReturnNullDirect) {
+  DoReturnNullTest(32 * 1024 * 1024, true);
+}
+
+TEST_F(PartitionReallocReturnNullTest, RepeatedReturnNull) {
+  DoReturnNullTest(512 * 1024, true);
+}
+
 #endif  // !defined(ARCH_CPU_64_BITS) || (defined(OS_POSIX) &&
         // !(defined(OS_FUCHSIA) || defined(OS_MACOSX) || defined(OS_ANDROID)))
 
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 6b268cdb..794584c 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -23,10 +23,11 @@
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/process/process_handle.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
@@ -41,35 +42,104 @@
 const DWORD kFileShareAll =
     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
 
+// Records a sample in a histogram named
+// "Windows.PostOperationState.|operation|" indicating the state of |path|
+// following the named operation. If |operation_succeeded| is true, the
+// "operation succeeded" sample is recorded. Otherwise, the state of |path| is
+// queried and the most meaningful sample is recorded.
+void RecordPostOperationState(const FilePath& path,
+                              StringPiece operation,
+                              bool operation_succeeded) {
+  // The state of a filesystem item after an operation.
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class PostOperationState {
+    kOperationSucceeded = 0,
+    kFileNotFoundAfterFailure = 1,
+    kPathNotFoundAfterFailure = 2,
+    kAccessDeniedAfterFailure = 3,
+    kNoAttributesAfterFailure = 4,
+    kEmptyDirectoryAfterFailure = 5,
+    kNonEmptyDirectoryAfterFailure = 6,
+    kNotDirectoryAfterFailure = 7,
+    kCount
+  } metric = PostOperationState::kOperationSucceeded;
+
+  if (!operation_succeeded) {
+    const DWORD attributes = ::GetFileAttributes(path.value().c_str());
+    if (attributes == INVALID_FILE_ATTRIBUTES) {
+      // On failure to delete, one might expect the file/directory to still be
+      // in place. Slice a failure to get its attributes into a few common error
+      // buckets.
+      const DWORD error_code = ::GetLastError();
+      if (error_code == ERROR_FILE_NOT_FOUND)
+        metric = PostOperationState::kFileNotFoundAfterFailure;
+      else if (error_code == ERROR_PATH_NOT_FOUND)
+        metric = PostOperationState::kPathNotFoundAfterFailure;
+      else if (error_code == ERROR_ACCESS_DENIED)
+        metric = PostOperationState::kAccessDeniedAfterFailure;
+      else
+        metric = PostOperationState::kNoAttributesAfterFailure;
+    } else if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
+      if (IsDirectoryEmpty(path))
+        metric = PostOperationState::kEmptyDirectoryAfterFailure;
+      else
+        metric = PostOperationState::kNonEmptyDirectoryAfterFailure;
+    } else {
+      metric = PostOperationState::kNotDirectoryAfterFailure;
+    }
+  }
+
+  std::string histogram_name = "Windows.PostOperationState.";
+  operation.AppendToString(&histogram_name);
+  UmaHistogramEnumeration(histogram_name, metric, PostOperationState::kCount);
+}
+
+// Records the sample |error| in a histogram named
+// "Windows.FilesystemError.|operation|".
+void RecordFilesystemError(StringPiece operation, DWORD error) {
+  std::string histogram_name = "Windows.FilesystemError.";
+  operation.AppendToString(&histogram_name);
+  UmaHistogramSparse(histogram_name, error);
+}
+
 // Deletes all files and directories in a path.
-// Returns false on the first failure it encounters.
-bool DeleteFileRecursive(const FilePath& path,
-                         const FilePath::StringType& pattern,
-                         bool recursive) {
+// Returns ERROR_SUCCESS on success or the Windows error code corresponding to
+// the first error encountered.
+DWORD DeleteFileRecursive(const FilePath& path,
+                          const FilePath::StringType& pattern,
+                          bool recursive) {
   FileEnumerator traversal(path, false,
                            FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
                            pattern);
-  bool success = true;
+  DWORD result = ERROR_SUCCESS;
   for (FilePath current = traversal.Next(); !current.empty();
        current = traversal.Next()) {
     // Try to clear the read-only bit if we find it.
     FileEnumerator::FileInfo info = traversal.GetInfo();
     if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
         (recursive || !info.IsDirectory())) {
-      SetFileAttributes(
+      ::SetFileAttributes(
           current.value().c_str(),
           info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
     }
 
+    DWORD this_result = ERROR_SUCCESS;
     if (info.IsDirectory()) {
-      if (recursive && (!DeleteFileRecursive(current, pattern, true) ||
-                        !RemoveDirectory(current.value().c_str())))
-        success = false;
+      if (recursive) {
+        this_result = DeleteFileRecursive(current, pattern, true);
+        if (this_result == ERROR_SUCCESS &&
+            !::RemoveDirectory(current.value().c_str())) {
+          this_result = ::GetLastError();
+        }
+      }
     } else if (!::DeleteFile(current.value().c_str())) {
-      success = false;
+      this_result = ::GetLastError();
     }
+    if (result == ERROR_SUCCESS)
+      result = this_result;
   }
-  return success;
+  return result;
 }
 
 // Appends |mode_char| to |mode| before the optional character set encoding; see
@@ -203,6 +273,55 @@
   return success;
 }
 
+// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
+DWORD DoDeleteFile(const FilePath& path, bool recursive) {
+  AssertBlockingAllowed();
+
+  if (path.empty())
+    return ERROR_SUCCESS;
+
+  if (path.value().length() >= MAX_PATH)
+    return ERROR_BAD_PATHNAME;
+
+  // Handle any path with wildcards.
+  if (path.BaseName().value().find_first_of(L"*?") !=
+      FilePath::StringType::npos) {
+    return DeleteFileRecursive(path.DirName(), path.BaseName().value(),
+                               recursive);
+  }
+
+  // Report success if the file or path does not exist.
+  const DWORD attr = ::GetFileAttributes(path.value().c_str());
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+    const DWORD error_code = ::GetLastError();
+    return (error_code == ERROR_FILE_NOT_FOUND ||
+            error_code == ERROR_PATH_NOT_FOUND)
+               ? ERROR_SUCCESS
+               : error_code;
+  }
+
+  // Clear the read-only bit if it is set.
+  if ((attr & FILE_ATTRIBUTE_READONLY) &&
+      !::SetFileAttributes(path.value().c_str(),
+                           attr & ~FILE_ATTRIBUTE_READONLY)) {
+    return ::GetLastError();
+  }
+
+  // Perform a simple delete on anything that isn't a directory.
+  if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+    return ::DeleteFile(path.value().c_str()) ? ERROR_SUCCESS
+                                              : ::GetLastError();
+  }
+
+  if (recursive) {
+    const DWORD error_code = DeleteFileRecursive(path, L"*", true);
+    if (error_code != ERROR_SUCCESS)
+      return error_code;
+  }
+  return ::RemoveDirectory(path.value().c_str()) ? ERROR_SUCCESS
+                                                 : ::GetLastError();
+}
+
 }  // namespace
 
 FilePath MakeAbsoluteFilePath(const FilePath& input) {
@@ -214,37 +333,22 @@
 }
 
 bool DeleteFile(const FilePath& path, bool recursive) {
+  static constexpr char kRecursive[] = "DeleteFile.Recursive";
+  static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
+  const StringPiece operation(recursive ? kRecursive : kNonRecursive);
+
   AssertBlockingAllowed();
 
-  if (path.empty())
+  // Metrics for delete failures tracked in https://crbug.com/599084. Delete may
+  // fail for a number of reasons. Log some metrics relating to failures in the
+  // current code so that any improvements or regressions resulting from
+  // subsequent code changes can be detected.
+  const DWORD error = DoDeleteFile(path, recursive);
+  RecordPostOperationState(path, operation, error == ERROR_SUCCESS);
+  if (error == ERROR_SUCCESS)
     return true;
 
-  if (path.value().length() >= MAX_PATH)
-    return false;
-
-  // Handle any path with wildcards.
-  if (path.BaseName().value().find_first_of(L"*?") !=
-      FilePath::StringType::npos) {
-    return DeleteFileRecursive(path.DirName(), path.BaseName().value(),
-                               recursive);
-  }
-  DWORD attr = GetFileAttributes(path.value().c_str());
-  // We're done if we can't find the path.
-  if (attr == INVALID_FILE_ATTRIBUTES)
-    return true;
-  // We may need to clear the read-only bit.
-  if ((attr & FILE_ATTRIBUTE_READONLY) &&
-      !SetFileAttributes(path.value().c_str(),
-                          attr & ~FILE_ATTRIBUTE_READONLY)) {
-    return false;
-  }
-  // Directories are handled differently if they're recursive.
-  if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
-    return !!::DeleteFile(path.value().c_str());
-  // Handle a simple, single file delete.
-  if (!recursive || DeleteFileRecursive(path, L"*", true))
-    return !!RemoveDirectory(path.value().c_str());
-
+  RecordFilesystemError(operation, error);
   return false;
 }
 
@@ -394,7 +498,7 @@
   // perform poorly when creating a large number of files with the same prefix.
   // In such cases, it is recommended that you construct unique file names based
   // on GUIDs."
-  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364991(v=vs.85).aspx
+  // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx
 
   FilePath temp_name;
   bool create_file_success = false;
diff --git a/base/i18n/number_formatting_unittest.cc b/base/i18n/number_formatting_unittest.cc
index 0bdd603..045bc0e 100644
--- a/base/i18n/number_formatting_unittest.cc
+++ b/base/i18n/number_formatting_unittest.cc
@@ -63,20 +63,20 @@
     {1024.2, 0, "1,024", "1.024"},
     {-1024.223, 2, "-1,024.22", "-1.024,22"},
     {std::numeric_limits<double>::max(), 6,
-        "179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
-        "000.000000",
-        "179.769.313.486.232.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
-        "000,000000"},
+     "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000.000000",
+     "179.769.313.486.231.570.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000,000000"},
     {std::numeric_limits<double>::min(), 2, "0.00", "0,00"},
     {-42.7, 3, "-42.700", "-42,700"},
   };
@@ -108,12 +108,10 @@
     const wchar_t* expected_persian;
     const wchar_t* expected_arabic;
   } cases[] = {
-    {0, "0%", L"0\xa0%", L"\x6f0\x66a", L"\x660\xa0\x66a\x61c"},
-    {42, "42%", L"42\xa0%", L"\x6f4\x6f2\x66a",
-        L"\x664\x662\xa0\x66a\x61c"},
-    {1024, "1,024%", L"1.024\xa0%",
-        L"\x6f1\x66c\x6f0\x6f2\x6f4\x66a",
-        L"\x661\x66c\x660\x662\x664\xa0\x66a\x61c"},
+      {0, "0%", L"0\xa0%", L"\x6f0\x66a", L"\x660\x66a\x61c"},
+      {42, "42%", L"42\xa0%", L"\x6f4\x6f2\x66a", L"\x664\x662\x66a\x61c"},
+      {1024, "1,024%", L"1.024\xa0%", L"\x6f1\x66c\x6f0\x6f2\x6f4\x66a",
+       L"\x661\x66c\x660\x662\x664\x66a\x61c"},
   };
 
   test::ScopedRestoreICUDefaultLocale restore_locale;
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index a7048568..f3b55f2 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -86,7 +86,7 @@
 const char kSignalFileTerm[] = "TerminatedChildProcess.die";
 
 #if defined(OS_FUCHSIA)
-const char kSignalFileClone[] = "/tmp/ClonedTmpDir.die";
+const char kSignalFileClone[] = "ClonedTmpDir.die";
 #endif
 #endif  // defined(OS_POSIX)
 
@@ -158,13 +158,13 @@
 };
 
 std::string ProcessUtilTest::GetSignalFilePath(const char* filename) {
-#if !defined(OS_ANDROID)
-  return filename;
-#else
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
   FilePath tmp_dir;
-  PathService::Get(DIR_CACHE, &tmp_dir);
+  PathService::Get(DIR_TEMP, &tmp_dir);
   tmp_dir = tmp_dir.Append(filename);
   return tmp_dir.value();
+#else
+  return filename;
 #endif
 }
 
@@ -932,30 +932,6 @@
   EXPECT_EQ(0, exit_code);
 }
 
-MULTIPROCESS_TEST_MAIN(ProcessUtilsVerifyNamespace) {
-  CHECK(PathExists(FilePath("/data")));
-  CHECK(!PathExists(FilePath("/svc")));
-  return 0;
-}
-
-TEST_F(ProcessUtilTest, LaunchNamespaceMap) {
-  LaunchOptions options;
-  options.clone_flags &= ~LP_CLONE_FDIO_NAMESPACE;
-  options.paths_to_map.push_back("/data");
-
-  // gtest uses /tmp to pass flags to the launched process, so it needs to be
-  // mapped as well.
-  options.paths_to_map.push_back("/tmp");
-
-  Process process =
-      SpawnChildWithOptions("ProcessUtilsVerifyNamespace", options);
-  ASSERT_TRUE(process.IsValid());
-
-  int exit_code;
-  ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
-                                             &exit_code));
-  EXPECT_EQ(0, exit_code);
-}
 #endif  // defined(OS_FUCHSIA)
 
 namespace {
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
index ce30000b..8a889c5 100644
--- a/base/test/test_mock_time_task_runner.cc
+++ b/base/test/test_mock_time_task_runner.cc
@@ -251,27 +251,45 @@
   while (!tasks_.empty()) {
     // It's safe to remove const and consume |task| here, since |task| is not
     // used for ordering the item.
-    tasks.push_back(
-        std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
+    if (!tasks_.top().task.IsCancelled()) {
+      tasks.push_back(
+          std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
+    }
     tasks_.pop();
   }
   return tasks;
 }
 
-bool TestMockTimeTaskRunner::HasPendingTask() const {
+bool TestMockTimeTaskRunner::HasPendingTask() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  AutoLock scoped_lock(tasks_lock_);
+  while (!tasks_.empty() && tasks_.top().task.IsCancelled())
+    tasks_.pop();
   return !tasks_.empty();
 }
 
-size_t TestMockTimeTaskRunner::GetPendingTaskCount() const {
+size_t TestMockTimeTaskRunner::GetPendingTaskCount() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  AutoLock scoped_lock(tasks_lock_);
+  TaskPriorityQueue preserved_tasks;
+  while (!tasks_.empty()) {
+    if (!tasks_.top().task.IsCancelled()) {
+      preserved_tasks.push(
+          std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
+    }
+    tasks_.pop();
+  }
+  tasks_.swap(preserved_tasks);
   return tasks_.size();
 }
 
-TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() const {
+TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  AutoLock scoped_lock(tasks_lock_);
+  while (!tasks_.empty() && tasks_.top().task.IsCancelled())
+    tasks_.pop();
   return tasks_.empty() ? TimeDelta::Max()
-                        : tasks_.top().GetTimeToRun() - NowTicks();
+                        : tasks_.top().GetTimeToRun() - now_ticks_;
 }
 
 // TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
@@ -329,6 +347,8 @@
     TestPendingTask task_info;
     if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
       break;
+    if (task_info.task.IsCancelled())
+      continue;
     // If tasks were posted with a negative delay, task_info.GetTimeToRun() will
     // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
     // moving the clock backwards in this case.
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
index fd2c2eeb..a7654318 100644
--- a/base/test/test_mock_time_task_runner.h
+++ b/base/test/test_mock_time_task_runner.h
@@ -171,10 +171,11 @@
   std::unique_ptr<TickClock> DeprecatedGetMockTickClock() const;
   const TickClock* GetMockTickClock() const;
 
+  // Cancelled pending tasks get pruned automatically.
   base::circular_deque<TestPendingTask> TakePendingTasks();
-  bool HasPendingTask() const;
-  size_t GetPendingTaskCount() const;
-  TimeDelta NextPendingTaskDelay() const;
+  bool HasPendingTask();
+  size_t GetPendingTaskCount();
+  TimeDelta NextPendingTaskDelay();
 
   // SingleThreadTaskRunner:
   bool RunsTasksInCurrentSequence() const override;
diff --git a/base/test/test_mock_time_task_runner_unittest.cc b/base/test/test_mock_time_task_runner_unittest.cc
index 06530ca..e149f65 100644
--- a/base/test/test_mock_time_task_runner_unittest.cc
+++ b/base/test/test_mock_time_task_runner_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/test/test_mock_time_task_runner.h"
 
+#include "base/cancelable_callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/test/gtest_util.h"
@@ -196,4 +197,54 @@
   run_loop.Run();
 }
 
+TEST(TestMockTimeTaskRunnerTest, TakePendingTasks) {
+  auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
+  task_runner->PostTask(FROM_HERE, Bind([]() {}));
+  EXPECT_TRUE(task_runner->HasPendingTask());
+  EXPECT_EQ(1u, task_runner->TakePendingTasks().size());
+  EXPECT_FALSE(task_runner->HasPendingTask());
+}
+
+TEST(TestMockTimeTaskRunnerTest, CancelPendingTask) {
+  auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
+  CancelableClosure task1(Bind([]() {}));
+  task_runner->PostDelayedTask(FROM_HERE, task1.callback(),
+                               TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(task_runner->HasPendingTask());
+  EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+  EXPECT_EQ(TimeDelta::FromSeconds(1), task_runner->NextPendingTaskDelay());
+  task1.Cancel();
+  EXPECT_FALSE(task_runner->HasPendingTask());
+
+  CancelableClosure task2(Bind([]() {}));
+  task_runner->PostDelayedTask(FROM_HERE, task2.callback(),
+                               TimeDelta::FromSeconds(1));
+  task2.Cancel();
+  EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
+
+  CancelableClosure task3(Bind([]() {}));
+  task_runner->PostDelayedTask(FROM_HERE, task3.callback(),
+                               TimeDelta::FromSeconds(1));
+  task3.Cancel();
+  EXPECT_EQ(TimeDelta::Max(), task_runner->NextPendingTaskDelay());
+
+  CancelableClosure task4(Bind([]() {}));
+  task_runner->PostDelayedTask(FROM_HERE, task4.callback(),
+                               TimeDelta::FromSeconds(1));
+  task4.Cancel();
+  EXPECT_TRUE(task_runner->TakePendingTasks().empty());
+}
+
+TEST(TestMockTimeTaskRunnerTest, NoFastForwardToCancelledTask) {
+  auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
+  TimeTicks start_time = task_runner->NowTicks();
+  CancelableClosure task(Bind([]() {}));
+  task_runner->PostDelayedTask(FROM_HERE, task.callback(),
+                               TimeDelta::FromSeconds(1));
+  EXPECT_EQ(TimeDelta::FromSeconds(1), task_runner->NextPendingTaskDelay());
+  task.Cancel();
+  task_runner->FastForwardUntilNoTasksRemain();
+  EXPECT_EQ(start_time, task_runner->NowTicks());
+}
+
 }  // namespace base
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index 3061a4b..cb05d60 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -854,16 +854,15 @@
   project_entries = []
   # When only one entry will be generated we want it to have a valid
   # build.gradle file with its own AndroidManifest.
-  add_all_module = not args.split_projects and len(entries) > 1
   for entry in entries:
     data = _GenerateGradleFile(
         entry, generator, build_vars, source_properties, jinja_processor)
-    if data and add_all_module:
+    if data and not args.all:
         project_entries.append((entry.ProjectName(), entry.GradleSubdir()))
         _WriteFile(
             os.path.join(generator.EntryOutputDir(entry), _GRADLE_BUILD_FILE),
             data)
-  if add_all_module:
+  if args.all:
     project_entries.append((_MODULE_ALL, _MODULE_ALL))
     _GenerateModuleAll(
         _gradle_output_dir, generator, build_vars, source_properties,
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py
index 906647b2..75aafc7 100755
--- a/build/android/gyp/apkbuilder.py
+++ b/build/android/gyp/apkbuilder.py
@@ -70,6 +70,10 @@
   parser.add_argument('--native-lib-placeholders',
                       help='GYP-list of native library placeholders to add.',
                       default='[]')
+  parser.add_argument('--secondary-native-lib-placeholders',
+                      help='GYP-list of native library placeholders to add '
+                           'for the secondary ABI',
+                      default='[]')
   parser.add_argument('--uncompress-shared-libraries',
                       action='store_true',
                       help='Uncompress shared libraries')
@@ -79,6 +83,8 @@
       options.uncompressed_assets)
   options.native_lib_placeholders = build_utils.ParseGnList(
       options.native_lib_placeholders)
+  options.secondary_native_lib_placeholders = build_utils.ParseGnList(
+      options.secondary_native_lib_placeholders)
   options.java_resources = build_utils.ParseGnList(options.java_resources)
   all_libs = []
   for gyp_list in options.native_libs:
@@ -93,7 +99,8 @@
   if not options.android_abi and (options.native_libs or
                                   options.native_lib_placeholders):
     raise Exception('Must specify --android-abi with --native-libs')
-  if not options.secondary_android_abi and options.secondary_native_libs:
+  if not options.secondary_android_abi and (options.secondary_native_libs or
+      options.secondary_native_lib_placeholders):
     raise Exception('Must specify --secondary-android-abi with'
                     ' --secondary-native-libs')
   return options
@@ -225,6 +232,7 @@
 
   input_strings = [options.android_abi,
                    options.native_lib_placeholders,
+                   options.secondary_native_lib_placeholders,
                    options.uncompress_shared_libraries]
 
   if options.secondary_android_abi:
@@ -304,6 +312,13 @@
           apk_path = 'lib/%s/%s' % (options.android_abi, name)
           build_utils.AddToZipHermetic(out_apk, apk_path, data='')
 
+        for name in sorted(options.secondary_native_lib_placeholders):
+          # Note: Empty libs files are ignored by md5check (can cause issues
+          # with stale builds when the only change is adding/removing
+          # placeholders).
+          apk_path = 'lib/%s/%s' % (options.secondary_android_abi, name)
+          build_utils.AddToZipHermetic(out_apk, apk_path, data='')
+
         # 5. Resources
         for info in resource_infos[1:]:
           copy_resource(info)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index e02e1fd..6068bfa 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2024,6 +2024,8 @@
   #   output_apk_path: Output path for the generated .apk.
   #   native_lib_placeholders: List of placeholder filenames to add to the apk
   #     (optional).
+  #   secondary_native_lib_placeholders: List of placeholder filenames to add to
+  #     the apk for the secondary ABI (optional).
   #   native_libs: List of native libraries.
   #   native_libs_filearg: @FileArg() of additionally native libraries.
   #   write_asset_list: Adds an extra file to the assets, which contains a list of
@@ -2040,6 +2042,11 @@
       if (defined(invoker.native_lib_placeholders)) {
         _native_lib_placeholders = invoker.native_lib_placeholders
       }
+      _secondary_native_lib_placeholders = []
+      if (defined(invoker.secondary_native_lib_placeholders)) {
+        _secondary_native_lib_placeholders =
+            invoker.secondary_native_lib_placeholders
+      }
 
       script = "//build/android/gyp/apkbuilder.py"
       depfile = "$target_gen_dir/$target_name.d"
@@ -2106,6 +2113,13 @@
           _native_lib_placeholders != []) {
         args += [ "--android-abi=$android_app_abi" ]
       }
+      if (defined(invoker.secondary_abi_native_libs_filearg) ||
+          (defined(invoker.secondary_native_libs) &&
+           invoker.secondary_native_libs != []) ||
+          _secondary_native_lib_placeholders != []) {
+        assert(defined(android_app_secondary_abi))
+        args += [ "--secondary-android-abi=$android_app_secondary_abi" ]
+      }
       if (invoker.native_libs != []) {
         _rebased_native_libs = rebase_path(invoker.native_libs, root_build_dir)
         args += [ "--native-libs=$_rebased_native_libs" ]
@@ -2116,23 +2130,18 @@
       if (_native_lib_placeholders != []) {
         args += [ "--native-lib-placeholders=$_native_lib_placeholders" ]
       }
+      if (_secondary_native_lib_placeholders != []) {
+        args += [ "--secondary-native-lib-placeholders=$_secondary_native_lib_placeholders" ]
+      }
 
       # TODO (michaelbai): Remove the secondary_native_libs variable.
       if (defined(invoker.secondary_abi_native_libs_filearg)) {
-        assert(defined(android_app_secondary_abi))
-        args += [
-          "--secondary-native-libs=${invoker.secondary_abi_native_libs_filearg}",
-          "--secondary-android-abi=$android_app_secondary_abi",
-        ]
+        args += [ "--secondary-native-libs=${invoker.secondary_abi_native_libs_filearg}" ]
       } else if (defined(invoker.secondary_native_libs) &&
                  invoker.secondary_native_libs != []) {
-        assert(defined(android_app_secondary_abi))
         inputs += invoker.secondary_native_libs
         _secondary_native_libs = rebase_path(invoker.secondary_native_libs)
-        args += [
-          "--secondary-native-libs=$_secondary_native_libs",
-          "--secondary-android-abi=$android_app_secondary_abi",
-        ]
+        args += [ "--secondary-native-libs=$_secondary_native_libs" ]
       }
 
       if (defined(invoker.uncompress_shared_libraries) &&
@@ -2299,6 +2308,7 @@
                                "native_lib_placeholders",
                                "native_libs_filearg",
                                "packaged_resources_path",
+                               "secondary_native_lib_placeholders",
                                "secondary_abi_native_libs_filearg",
                                "secondary_native_libs",
                                "uncompress_shared_libraries",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 49d5960..97ad5f9b 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1780,6 +1780,8 @@
   #     dependencies will also be included in the apk (e.g. for is_component_build).
   #   native_lib_placeholders: List of placeholder filenames to add to the apk
   #     (optional).
+  #   secondary_native_lib_placeholders: List of placeholder filenames to add to
+  #     the apk for the secondary ABI (optional).
   #   apk_under_test: For an instrumentation test apk, this is the target of the
   #     tested apk.
   #   write_asset_list: Adds an extra file to the assets, which contains a list of
@@ -2520,7 +2522,11 @@
 
       # Placeholders necessary for some older devices.
       # http://crbug.com/395038
-      forward_variables_from(invoker, [ "native_lib_placeholders" ])
+      forward_variables_from(invoker,
+                             [
+                               "native_lib_placeholders",
+                               "secondary_native_lib_placeholders",
+                             ])
     }
 
     _write_installer_json_rule_name = "${_template_name}__incremental_json"
diff --git a/build/config/fuchsia/build_manifest.py b/build/config/fuchsia/build_manifest.py
index e5256fb..0fc8b74 100644
--- a/build/config/fuchsia/build_manifest.py
+++ b/build/config/fuchsia/build_manifest.py
@@ -24,11 +24,11 @@
 import tempfile
 
 
-def ReadDynamicLibDeps(path):
+def ReadDynamicLibDeps(paths):
   """Returns a list of NEEDED libraries read from a binary's ELF header."""
 
   LIBRARY_RE = re.compile(r'.*\(NEEDED\)\s+Shared library: \[(?P<lib>.*)\]')
-  elfinfo = subprocess.check_output(['readelf', '-d', path],
+  elfinfo = subprocess.check_output(['readelf', '-d'] + paths,
                                     stderr=open(os.devnull, 'w'))
   libs = []
   for line in elfinfo.split('\n'):
@@ -56,16 +56,15 @@
   # The computed set of visited transitive dependencies.
   deps = set()
 
-  while len(to_visit) > 0:
-    cur_path = to_visit.pop()
-    deps.add(cur_path)
+  while to_visit:
+    deps = deps.union(to_visit)
 
     # Resolve the full paths for all of |cur_path|'s NEEDED libraries.
     dep_paths = {available_libs[dep]
-                 for dep in ReadDynamicLibDeps(cur_path)}
+                 for dep in ReadDynamicLibDeps(list(to_visit))}
 
     # Add newly discovered dependencies to the pending traversal stack.
-    to_visit += dep_paths.difference(deps)
+    to_visit = dep_paths.difference(deps)
 
   return deps
 
@@ -222,7 +221,6 @@
         "%s: %s" % (os.path.relpath(output_path, out_dir),
                     " ".join([os.path.relpath(f, out_dir)
                               for f in expanded_files])))
-
   return 0
 
 
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 52f49a8..049a1f1 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -61,7 +61,8 @@
   # Enable checks for indirect function calls via a function pointer.
   # TODO(pcc): remove this when we're ready to add these checks by default.
   # https://crbug.com/701919
-  use_cfi_icall = false
+  use_cfi_icall = target_os == "linux" && !is_chromeos && target_cpu == "x64" &&
+                  is_official_build
 
   # Print detailed diagnostics when Control Flow Integrity detects a violation.
   use_cfi_diag = false
diff --git a/cc/blink/BUILD.gn b/cc/blink/BUILD.gn
index 95d4b32..b5b3741 100644
--- a/cc/blink/BUILD.gn
+++ b/cc/blink/BUILD.gn
@@ -15,8 +15,6 @@
     "web_compositor_support_impl.h",
     "web_content_layer_impl.cc",
     "web_content_layer_impl.h",
-    "web_external_texture_layer_impl.cc",
-    "web_external_texture_layer_impl.h",
     "web_image_layer_impl.cc",
     "web_image_layer_impl.h",
     "web_layer_impl.cc",
diff --git a/cc/blink/web_compositor_support_impl.cc b/cc/blink/web_compositor_support_impl.cc
index e70b25a..aca0e695 100644
--- a/cc/blink/web_compositor_support_impl.cc
+++ b/cc/blink/web_compositor_support_impl.cc
@@ -7,14 +7,12 @@
 #include <utility>
 
 #include "cc/blink/web_content_layer_impl.h"
-#include "cc/blink/web_external_texture_layer_impl.h"
 #include "cc/blink/web_image_layer_impl.h"
 #include "cc/blink/web_layer_impl.h"
 #include "cc/blink/web_scrollbar_layer_impl.h"
 #include "cc/layers/layer.h"
 
 using blink::WebContentLayer;
-using blink::WebExternalTextureLayer;
 using blink::WebImageLayer;
 using blink::WebLayer;
 using blink::WebScrollbar;
@@ -42,12 +40,6 @@
   return std::make_unique<WebContentLayerImpl>(client);
 }
 
-std::unique_ptr<WebExternalTextureLayer>
-WebCompositorSupportImpl::CreateExternalTextureLayer(
-    cc::TextureLayerClient* client) {
-  return std::make_unique<WebExternalTextureLayerImpl>(client);
-}
-
 std::unique_ptr<blink::WebImageLayer>
 WebCompositorSupportImpl::CreateImageLayer() {
   return std::make_unique<WebImageLayerImpl>();
diff --git a/cc/blink/web_compositor_support_impl.h b/cc/blink/web_compositor_support_impl.h
index c243bfef..518b4ea 100644
--- a/cc/blink/web_compositor_support_impl.h
+++ b/cc/blink/web_compositor_support_impl.h
@@ -27,8 +27,6 @@
   std::unique_ptr<blink::WebLayer> CreateLayerFromCCLayer(cc::Layer*) override;
   std::unique_ptr<blink::WebContentLayer> CreateContentLayer(
       cc::ContentLayerClient* client) override;
-  std::unique_ptr<blink::WebExternalTextureLayer> CreateExternalTextureLayer(
-      cc::TextureLayerClient* client) override;
   std::unique_ptr<blink::WebImageLayer> CreateImageLayer() override;
   std::unique_ptr<blink::WebScrollbarLayer> CreateScrollbarLayer(
       std::unique_ptr<blink::WebScrollbar> scrollbar,
diff --git a/cc/blink/web_external_texture_layer_impl.cc b/cc/blink/web_external_texture_layer_impl.cc
deleted file mode 100644
index 64fbf26..0000000
--- a/cc/blink/web_external_texture_layer_impl.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2014 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 "cc/blink/web_external_texture_layer_impl.h"
-
-#include "cc/blink/web_layer_impl.h"
-#include "cc/layers/texture_layer.h"
-
-using cc::TextureLayer;
-
-namespace cc_blink {
-
-WebExternalTextureLayerImpl::WebExternalTextureLayerImpl(
-    cc::TextureLayerClient* client) {
-  scoped_refptr<TextureLayer> layer = TextureLayer::CreateForMailbox(client);
-  layer->SetIsDrawable(true);
-  layer_.reset(new WebLayerImpl(layer));
-}
-
-WebExternalTextureLayerImpl::~WebExternalTextureLayerImpl() {
-  static_cast<TextureLayer*>(layer_->layer())->ClearClient();
-}
-
-blink::WebLayer* WebExternalTextureLayerImpl::Layer() {
-  return layer_.get();
-}
-
-void WebExternalTextureLayerImpl::ClearTexture() {
-  TextureLayer* layer = static_cast<TextureLayer*>(layer_->layer());
-  layer->ClearTexture();
-}
-
-void WebExternalTextureLayerImpl::SetOpaque(bool opaque) {
-  static_cast<TextureLayer*>(layer_->layer())->SetContentsOpaque(opaque);
-}
-
-void WebExternalTextureLayerImpl::SetFlipped(bool flipped) {
-  static_cast<TextureLayer*>(layer_->layer())->SetFlipped(flipped);
-}
-
-void WebExternalTextureLayerImpl::SetPremultipliedAlpha(
-    bool premultiplied_alpha) {
-  static_cast<TextureLayer*>(layer_->layer())
-      ->SetPremultipliedAlpha(premultiplied_alpha);
-}
-
-void WebExternalTextureLayerImpl::SetBlendBackgroundColor(bool blend) {
-  static_cast<TextureLayer*>(layer_->layer())->SetBlendBackgroundColor(blend);
-}
-
-void WebExternalTextureLayerImpl::SetNearestNeighbor(bool nearest_neighbor) {
-  static_cast<TextureLayer*>(layer_->layer())
-      ->SetNearestNeighbor(nearest_neighbor);
-}
-
-void WebExternalTextureLayerImpl::SetUV(
-    const blink::WebFloatPoint left_top,
-    const blink::WebFloatPoint right_bottom) {
-  static_cast<TextureLayer*>(layer_->layer())
-      ->SetUV(gfx::PointF(left_top.x, left_top.y),
-              gfx::PointF(right_bottom.x, right_bottom.y));
-}
-
-}  // namespace cc_blink
diff --git a/cc/blink/web_external_texture_layer_impl.h b/cc/blink/web_external_texture_layer_impl.h
deleted file mode 100644
index 11ca63c..0000000
--- a/cc/blink/web_external_texture_layer_impl.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 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 CC_BLINK_WEB_EXTERNAL_TEXTURE_LAYER_IMPL_H_
-#define CC_BLINK_WEB_EXTERNAL_TEXTURE_LAYER_IMPL_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "cc/blink/cc_blink_export.h"
-#include "third_party/blink/public/platform/web_external_texture_layer.h"
-
-namespace cc {
-class TextureLayerClient;
-}
-
-namespace cc_blink {
-class WebLayerImpl;
-
-class WebExternalTextureLayerImpl : public blink::WebExternalTextureLayer {
- public:
-  CC_BLINK_EXPORT explicit WebExternalTextureLayerImpl(cc::TextureLayerClient*);
-  ~WebExternalTextureLayerImpl() override;
-
-  // blink::WebExternalTextureLayer implementation.
-  blink::WebLayer* Layer() override;
-  void ClearTexture() override;
-  void SetOpaque(bool opaque) override;
-  void SetFlipped(bool flipped) override;
-  void SetPremultipliedAlpha(bool premultiplied) override;
-  void SetBlendBackgroundColor(bool blend) override;
-  void SetNearestNeighbor(bool nearest_neighbor) override;
-  void SetUV(const blink::WebFloatPoint left_top,
-             const blink::WebFloatPoint right_bottom) override;
-
- private:
-  std::unique_ptr<WebLayerImpl> layer_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebExternalTextureLayerImpl);
-};
-
-}  // namespace cc_blink
-
-#endif  // CC_BLINK_WEB_EXTERNAL_TEXTURE_LAYER_IMPL_H_
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 94935c0..131182a 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -1351,7 +1351,7 @@
 
     layer_tree_host_->SetViewportSizeAndScale(
         layer_tree_host_->device_viewport_size(), test_scale,
-        layer_tree_host_->local_surface_id());
+        layer_tree_host_->local_surface_id_from_parent());
 
     scrollbar_layer->Update();
 
@@ -1417,7 +1417,7 @@
 
     layer_tree_host_->SetViewportSizeAndScale(
         layer_tree_host_->device_viewport_size(), test_scale,
-        layer_tree_host_->local_surface_id());
+        layer_tree_host_->local_surface_id_from_parent());
 
     scrollbar_layer->Update();
 
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index bea6daa..92306d62 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -77,6 +77,8 @@
     "render_surface_filters.h",
     "scoped_raster_flags.cc",
     "scoped_raster_flags.h",
+    "shader_transfer_cache_entry.cc",
+    "shader_transfer_cache_entry.h",
     "skia_paint_canvas.cc",
     "skia_paint_canvas.h",
     "skia_paint_image_generator.cc",
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index 414e598..805520f 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -15,8 +15,8 @@
 
 namespace cc {
 namespace {
-base::AtomicSequenceNumber g_next_id_;
-base::AtomicSequenceNumber g_next_content_id_;
+base::AtomicSequenceNumber g_next_image_id;
+base::AtomicSequenceNumber g_next_image_content_id;
 }  // namespace
 
 const PaintImage::Id PaintImage::kNonLazyStableId = -1;
@@ -74,12 +74,12 @@
 
 // static
 PaintImage::Id PaintImage::GetNextId() {
-  return g_next_id_.GetNext();
+  return g_next_image_id.GetNext();
 }
 
 // static
 PaintImage::ContentId PaintImage::GetNextContentId() {
-  return g_next_content_id_.GetNext();
+  return g_next_image_content_id.GetNext();
 }
 
 const sk_sp<SkImage>& PaintImage::GetSkImage() const {
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index bb32eab..db168437 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -2282,10 +2282,10 @@
 
     if (op->IsPaintOpWithFlags()) {
       const auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
-      const bool create_skia_shaders = true;
+      const bool is_rasterizing = true;
       const ScopedRasterFlags scoped_flags(
           &flags_op->flags, new_params.image_provider, canvas->getTotalMatrix(),
-          iter.alpha(), create_skia_shaders);
+          iter.alpha(), is_rasterizing);
       if (const auto* raster_flags = scoped_flags.flags())
         flags_op->RasterWithFlags(canvas, raster_flags, new_params);
     } else {
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 42cd33d..050efd6 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -28,6 +28,7 @@
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkScalar.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
+#include "ui/gfx/color_space.h"
 
 class SkColorSpace;
 class SkStrikeClient;
@@ -168,6 +169,7 @@
     DeserializeOptions(TransferCacheDeserializeHelper* transfer_cache,
                        SkStrikeClient* strike_client);
     TransferCacheDeserializeHelper* transfer_cache = nullptr;
+    uint32_t raster_color_space_id = gfx::ColorSpace::kInvalidId;
     SkStrikeClient* strike_client = nullptr;
   };
 
diff --git a/cc/paint/paint_op_buffer_serializer.cc b/cc/paint/paint_op_buffer_serializer.cc
index e34eaaa..9b1453e7 100644
--- a/cc/paint/paint_op_buffer_serializer.cc
+++ b/cc/paint/paint_op_buffer_serializer.cc
@@ -272,11 +272,11 @@
     uint8_t alpha) {
   // We don't need the skia backing for decoded shaders during serialization,
   // since those are created on the service side where the record is rasterized.
-  const bool create_skia_shaders = false;
+  const bool is_rasterizing = false;
 
   const ScopedRasterFlags scoped_flags(
       &flags_op->flags, options->image_provider,
-      options->canvas->getTotalMatrix(), alpha, create_skia_shaders);
+      options->canvas->getTotalMatrix(), alpha, is_rasterizing);
   const PaintFlags* flags_to_serialize = scoped_flags.flags();
   if (!flags_to_serialize)
     return true;
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 4e8da84..c63a1cd 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -12,6 +12,7 @@
 #include "cc/paint/paint_op_buffer_serializer.h"
 #include "cc/paint/paint_op_reader.h"
 #include "cc/paint/paint_op_writer.h"
+#include "cc/paint/shader_transfer_cache_entry.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/paint_op_helper.h"
 #include "cc/test/skia_common.h"
@@ -3089,6 +3090,176 @@
   EXPECT_EQ(scale.height(), 0.8f);
 }
 
+TEST(PaintOpBufferTest, RecordShadersCached) {
+  auto record_buffer = sk_make_sp<PaintOpBuffer>();
+  record_buffer->push<DrawImageOp>(
+      CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f, nullptr);
+  auto shader = PaintShader::MakePaintRecord(
+      record_buffer, SkRect::MakeWH(10.f, 10.f),
+      SkShader::TileMode::kRepeat_TileMode,
+      SkShader::TileMode::kRepeat_TileMode, nullptr);
+  shader->set_has_animated_images(false);
+  auto shader_id = shader->paint_record_shader_id();
+  TestOptionsProvider options_provider;
+  auto* transfer_cache = options_provider.transfer_cache_helper();
+
+  // Generate serialized |memory|.
+  std::unique_ptr<char, base::AlignedFreeDeleter> memory(
+      static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
+                                            PaintOpBuffer::PaintOpAlign)));
+  size_t memory_written = 0;
+  {
+    auto buffer = sk_make_sp<PaintOpBuffer>();
+    PaintFlags flags;
+    flags.setShader(shader);
+    buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
+
+    SimpleBufferSerializer serializer(
+        memory.get(), PaintOpBuffer::kInitialBufferSize,
+        options_provider.image_provider(), transfer_cache,
+        options_provider.strike_server(), options_provider.color_space(),
+        options_provider.can_use_lcd_text());
+    serializer.Serialize(buffer.get());
+    memory_written = serializer.written();
+  }
+
+  // Generate serialized |memory_scaled|, which is the same pob, but with
+  // a scale factor.
+  std::unique_ptr<char, base::AlignedFreeDeleter> memory_scaled(
+      static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
+                                            PaintOpBuffer::PaintOpAlign)));
+  size_t memory_scaled_written = 0;
+  {
+    auto buffer = sk_make_sp<PaintOpBuffer>();
+    PaintFlags flags;
+    flags.setShader(shader);
+    // This buffer has an additional scale op.
+    buffer->push<ScaleOp>(2.0f, 3.7f);
+    buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
+
+    SimpleBufferSerializer serializer(
+        memory_scaled.get(), PaintOpBuffer::kInitialBufferSize,
+        options_provider.image_provider(), transfer_cache,
+        options_provider.strike_server(), options_provider.color_space(),
+        options_provider.can_use_lcd_text());
+    serializer.Serialize(buffer.get());
+    memory_scaled_written = serializer.written();
+  }
+
+  // Hold onto records so PaintShader pointer comparisons are valid.
+  sk_sp<PaintRecord> records[5];
+  const SkShader* last_shader = nullptr;
+  PaintOp::DeserializeOptions deserialize_options(
+      transfer_cache, options_provider.strike_client());
+
+  // Several deserialization test cases:
+  // (0) deserialize once, verify cached is the same as deserialized version
+  // (1) deserialize again, verify shader gets reused
+  // (2) change color space, verify shader is new
+  // (3) change scale, verify shader is new
+  // (4) sanity check, same new scale + same new colorspace, shader is reused.
+  for (size_t i = 0; i < 5; ++i) {
+    if (i < 2) {
+      // arbitrary color space ids
+      deserialize_options.raster_color_space_id = 23;
+    } else {
+      deserialize_options.raster_color_space_id = 34;
+    }
+
+    if (i < 3) {
+      records[i] = PaintOpBuffer::MakeFromMemory(memory.get(), memory_written,
+                                                 deserialize_options);
+    } else {
+      records[i] = PaintOpBuffer::MakeFromMemory(
+          memory_scaled.get(), memory_scaled_written, deserialize_options);
+    }
+
+    auto* entry =
+        transfer_cache->GetEntryAs<ServiceShaderTransferCacheEntry>(shader_id);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry->raster_color_space_id(),
+              deserialize_options.raster_color_space_id);
+    if (i < 3)
+      EXPECT_EQ(records[i]->size(), 1u);
+    else
+      EXPECT_EQ(records[i]->size(), 2u);
+
+    for (auto* base_op : PaintOpBuffer::Iterator(records[i].get())) {
+      if (base_op->GetType() != PaintOpType::DrawRect)
+        continue;
+      auto* op = static_cast<const DrawRectOp*>(base_op);
+
+      // In every case, the shader in the op should get cached for future
+      // use.
+      auto* op_skshader = op->flags.getShader()->GetSkShader().get();
+      EXPECT_EQ(op_skshader, entry->shader()->GetSkShader().get());
+      switch (i) {
+        case 0:
+          // Nothing to check.
+          break;
+        case 1:
+          EXPECT_EQ(op_skshader, last_shader);
+          break;
+        case 2:
+          EXPECT_NE(op_skshader, last_shader);
+          break;
+        case 3:
+          EXPECT_NE(op_skshader, last_shader);
+          break;
+        case 4:
+          EXPECT_EQ(op_skshader, last_shader);
+          break;
+      }
+      last_shader = op_skshader;
+    }
+  }
+}
+
+TEST(PaintOpBufferTest, RecordShadersCachedSize) {
+  auto record_buffer = sk_make_sp<PaintOpBuffer>();
+  size_t estimated_image_size = 30 * 30 * 4;
+  auto image = CreateBitmapImage(gfx::Size(30, 30));
+  record_buffer->push<DrawImageOp>(image, 0.f, 0.f, nullptr);
+  auto shader = PaintShader::MakePaintRecord(
+      record_buffer, SkRect::MakeWH(10.f, 10.f),
+      SkShader::TileMode::kRepeat_TileMode,
+      SkShader::TileMode::kRepeat_TileMode, nullptr);
+  shader->set_has_animated_images(false);
+  auto shader_id = shader->paint_record_shader_id();
+  TestOptionsProvider options_provider;
+  auto* transfer_cache = options_provider.transfer_cache_helper();
+
+  // Generate serialized |memory|.
+  std::unique_ptr<char, base::AlignedFreeDeleter> memory(
+      static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
+                                            PaintOpBuffer::PaintOpAlign)));
+  auto buffer = sk_make_sp<PaintOpBuffer>();
+  PaintFlags flags;
+  flags.setShader(shader);
+  buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags);
+
+  SimpleBufferSerializer serializer(
+      memory.get(), PaintOpBuffer::kInitialBufferSize,
+      options_provider.image_provider(), transfer_cache,
+      options_provider.strike_server(), options_provider.color_space(),
+      options_provider.can_use_lcd_text());
+  serializer.Serialize(buffer.get());
+
+  PaintOp::DeserializeOptions deserialize_options(
+      transfer_cache, options_provider.strike_client());
+  auto record = PaintOpBuffer::MakeFromMemory(
+      memory.get(), serializer.written(), deserialize_options);
+  auto* shader_entry =
+      transfer_cache->GetEntryAs<ServiceShaderTransferCacheEntry>(shader_id);
+  ASSERT_TRUE(shader_entry);
+
+  // The size of the shader in the cache should be bigger than both the record
+  // and the image.  Exact numbers not used here to not overfit this test.
+  size_t shader_size = shader_entry->CachedSize();
+  EXPECT_GT(estimated_image_size, serializer.written());
+  EXPECT_GT(shader_size, estimated_image_size);
+}
+
 TEST(PaintOpBufferTest, TotalOpCount) {
   auto record_buffer = sk_make_sp<PaintOpBuffer>();
   auto sub_record_buffer = sk_make_sp<PaintOpBuffer>();
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 3431afc3..915bb86 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -15,6 +15,7 @@
 #include "cc/paint/paint_shader.h"
 #include "cc/paint/paint_typeface_transfer_cache_entry.h"
 #include "cc/paint/path_transfer_cache_entry.h"
+#include "cc/paint/shader_transfer_cache_entry.h"
 #include "cc/paint/transfer_cache_deserialize_helper.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkRRect.h"
@@ -449,8 +450,18 @@
   Read(&ref.image_);
   bool has_record = false;
   ReadSimple(&has_record);
-  if (has_record)
-    Read(&ref.record_);
+  uint32_t shader_id = PaintShader::kInvalidRecordShaderId;
+  size_t shader_size = 0;
+  if (has_record) {
+    Read(&shader_id);
+
+    // Track dependent transfer cache entries to make cached shader size
+    // more realistic.
+    size_t pre_size = options_.transfer_cache->GetTotalEntrySizes();
+    size_t record_size = Read(&ref.record_);
+    size_t post_size = options_.transfer_cache->GetTotalEntrySizes();
+    shader_size = post_size - pre_size + record_size;
+  }
   decltype(ref.colors_)::size_type colors_size = 0;
   ReadSimple(&colors_size);
 
@@ -488,9 +499,40 @@
     SetInvalid();
     return;
   }
-  // TODO(vmpstr): We should have a PaintShader id and cache these shaders
-  // instead of creating every time we deserialize.
-  (*shader)->CreateSkShader();
+
+  if (shader_id == PaintShader::kInvalidRecordShaderId) {
+    // Paint record shaders must have ids.
+    if (shader_type == PaintShader::Type::kPaintRecord) {
+      SetInvalid();
+      return;
+    }
+    (*shader)->CreateSkShader();
+    return;
+  }
+
+  // Record shaders have shader ids.  Attempt to use cached versions of
+  // these so that Skia can cache based on SkPictureShader::fUniqueId.
+  // These shaders are always serialized (and assumed to not be large
+  // records).  Handling this edge case in this roundabout way prevents
+  // transfer cache entries from needing to depend on other transfer cache
+  // entries.
+  auto* entry =
+      options_.transfer_cache->GetEntryAs<ServiceShaderTransferCacheEntry>(
+          shader_id);
+  // Only consider entries that use the same scale and color space.
+  // This limits the service side transfer cache to only having one entry
+  // per shader but this will hit the common case of enabling Skia reuse.
+  if (entry && entry->shader()->tile_ == ref.tile_ &&
+      entry->raster_color_space_id() == options_.raster_color_space_id) {
+    DCHECK(!ref.cached_shader_);
+    ref.cached_shader_ = entry->shader()->GetSkShader();
+  } else {
+    ref.CreateSkShader();
+    std::unique_ptr<ServiceShaderTransferCacheEntry> entry(
+        new ServiceShaderTransferCacheEntry(
+            *shader, options_.raster_color_space_id, shader_size));
+    options_.transfer_cache->CreateLocalEntry(shader_id, std::move(entry));
+  }
 }
 
 void PaintOpReader::Read(SkMatrix* matrix) {
@@ -1149,7 +1191,7 @@
       base::OptionalOrNullptr(crop_rect)));
 }
 
-void PaintOpReader::Read(sk_sp<PaintRecord>* record) {
+size_t PaintOpReader::Read(sk_sp<PaintRecord>* record) {
   size_t size_bytes = 0;
   ReadSimple(&size_bytes);
   AlignMemory(PaintOpBuffer::PaintOpAlign);
@@ -1159,16 +1201,16 @@
     // enabled.
     if (size_bytes != 0) {
       SetInvalid();
-      return;
+      return 0;
     }
     *record = sk_make_sp<PaintOpBuffer>();
-    return;
+    return 0;
   }
 
   if (size_bytes > remaining_bytes_)
     SetInvalid();
   if (!valid_)
-    return;
+    return 0;
 
   PaintOp::DeserializeOptions options;
   options.transfer_cache = options_.transfer_cache;
@@ -1176,10 +1218,11 @@
   *record = PaintOpBuffer::MakeFromMemory(memory_, size_bytes, options);
   if (!*record) {
     SetInvalid();
-    return;
+    return 0;
   }
   memory_ += size_bytes;
   remaining_bytes_ -= size_bytes;
+  return size_bytes;
 }
 
 void PaintOpReader::Read(SkRegion* region) {
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index bfed664..6a0c0e7 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -183,7 +183,9 @@
       sk_sp<PaintFilter>* filter,
       const base::Optional<PaintFilter::CropRect>& crop_rect);
 
-  void Read(sk_sp<PaintRecord>* record);
+  // Returns the size of the read record, 0 if error.
+  size_t Read(sk_sp<PaintRecord>* record);
+
   void Read(SkRegion* region);
 
   const volatile char* memory_ = nullptr;
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index d51b845..68ac8c31 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -327,6 +327,8 @@
   Write(shader->image_);
   if (shader->record_) {
     Write(true);
+    DCHECK_NE(shader->id_, PaintShader::kInvalidRecordShaderId);
+    Write(shader->id_);
     base::Optional<gfx::Rect> playback_rect;
     base::Optional<gfx::SizeF> post_scale;
     if (shader->tile_scale()) {
@@ -337,6 +339,7 @@
     Write(shader->record_.get(), std::move(playback_rect),
           std::move(post_scale));
   } else {
+    DCHECK_EQ(shader->id_, PaintShader::kInvalidRecordShaderId);
     Write(false);
   }
   WriteSimple(shader->colors_.size());
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
index 6baee9f..45f067d4f 100644
--- a/cc/paint/paint_shader.cc
+++ b/cc/paint/paint_shader.cc
@@ -4,6 +4,7 @@
 
 #include "cc/paint/paint_shader.h"
 
+#include "base/atomic_sequence_num.h"
 #include "cc/paint/paint_op_writer.h"
 #include "cc/paint/paint_record.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
@@ -11,6 +12,7 @@
 
 namespace cc {
 namespace {
+base::AtomicSequenceNumber g_next_shader_id;
 
 sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record,
                              const SkRect& bounds,
@@ -26,6 +28,8 @@
 
 }  // namespace
 
+const PaintShader::RecordShaderId PaintShader::kInvalidRecordShaderId = -1;
+
 sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
 
@@ -151,6 +155,7 @@
   sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
 
   shader->record_ = std::move(record);
+  shader->id_ = g_next_shader_id.GetNext();
   shader->tile_ = tile;
   shader->scaling_behavior_ = scaling_behavior;
   shader->SetMatrixAndTiling(local_matrix, tx, ty);
@@ -175,6 +180,7 @@
          sizeof(shader->end_degrees_) +
          PaintOpWriter::GetImageSize(shader->image_) +
          PaintOpWriter::GetImageSize(shader->image_) + bool_size +
+         sizeof(shader->id_) +
          PaintOpWriter::GetRecordSize(shader->record_.get()) +
          sizeof(shader->colors_.size()) +
          shader->colors_.size() * sizeof(SkColor) +
@@ -258,6 +264,7 @@
 
   sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
   shader->record_ = record_;
+  shader->id_ = id_;
   shader->tile_ = tile_rect;
   // Use a fixed scale since we have already scaled the tile rect and fixed the
   // raster scale.
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 6c197b7..d231174 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -16,6 +16,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkScalar.h"
 #include "third_party/skia/include/core/SkShader.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size_f.h"
 
 namespace cc {
@@ -36,6 +37,9 @@
     kShaderCount
   };
 
+  using RecordShaderId = uint32_t;
+  static const RecordShaderId kInvalidRecordShaderId;
+
   // Scaling behavior dictates how a PaintRecord shader will behave. Use
   // RasterAtScale to create a picture shader. Use FixedScale to create an image
   // shader that is backed by the paint record.
@@ -148,6 +152,11 @@
   bool operator==(const PaintShader& other) const;
   bool operator!=(const PaintShader& other) const { return !(*this == other); }
 
+  RecordShaderId paint_record_shader_id() const {
+    DCHECK(id_ == kInvalidRecordShaderId || shader_type_ == Type::kPaintRecord);
+    return id_;
+  }
+
  private:
   friend class PaintFlags;
   friend class PaintOpReader;
@@ -156,6 +165,7 @@
   friend class ScopedRasterFlags;
   FRIEND_TEST_ALL_PREFIXES(PaintShaderTest, DecodePaintRecord);
   FRIEND_TEST_ALL_PREFIXES(PaintOpBufferTest, PaintRecordShaderSerialization);
+  FRIEND_TEST_ALL_PREFIXES(PaintOpBufferTest, RecordShadersCached);
 
   explicit PaintShader(Type type);
 
@@ -196,6 +206,7 @@
 
   PaintImage image_;
   sk_sp<PaintRecord> record_;
+  RecordShaderId id_ = kInvalidRecordShaderId;
 
   // For decoded PaintRecord shaders, specifies the scale at which the record
   // will be rasterized.
diff --git a/cc/paint/scoped_raster_flags.cc b/cc/paint/scoped_raster_flags.cc
index b54f137..e4a6298 100644
--- a/cc/paint/scoped_raster_flags.cc
+++ b/cc/paint/scoped_raster_flags.cc
@@ -13,7 +13,7 @@
                                      ImageProvider* image_provider,
                                      const SkMatrix& ctm,
                                      uint8_t alpha,
-                                     bool create_skia_shader)
+                                     bool is_rasterizing)
     : original_flags_(flags) {
   if (flags->HasDiscardableImages() && image_provider) {
     // TODO(khushalsagar): The decoding of images in PaintFlags here is a bit of
@@ -25,7 +25,7 @@
     DecodeImageShader(ctm);
     if (decode_failed_)
       return;
-    DecodeRecordShader(ctm, create_skia_shader);
+    DecodeRecordShader(ctm, is_rasterizing);
     if (decode_failed_)
       return;
     DecodeFilter();
@@ -101,16 +101,20 @@
 }
 
 void ScopedRasterFlags::DecodeRecordShader(const SkMatrix& ctm,
-                                           bool create_skia_shader) {
+                                           bool is_rasterizing) {
   if (!flags()->HasShader() ||
       flags()->getShader()->shader_type() != PaintShader::Type::kPaintRecord)
     return;
 
-  // TODO(khushalsagar): For OOP, we have to decode everything during
-  // serialization. This will force us to use original sized decodes.
-  if (flags()->getShader()->image_analysis_state() !=
-      ImageAnalysisState::kAnimatedImages)
+  // Only early out here if we are rasterizing and not serializing directly.
+  // In process raster will cache the skia shader by letting Skia handle
+  // the decodes as needed.  Out of process raster should never early out
+  // and should always make sure the fonts and images are processed at the
+  // correct scale.
+  if (is_rasterizing && flags()->getShader()->image_analysis_state() !=
+                            ImageAnalysisState::kAnimatedImages) {
     return;
+  }
 
   auto decoded_shader = flags()->getShader()->CreateDecodedPaintRecord(
       ctm, &*decode_stashing_image_provider_);
@@ -119,7 +123,7 @@
     return;
   }
 
-  if (create_skia_shader)
+  if (is_rasterizing)
     decoded_shader->CreateSkShader(&*decode_stashing_image_provider_);
   MutableFlags()->setShader(std::move(decoded_shader));
 }
diff --git a/cc/paint/scoped_raster_flags.h b/cc/paint/scoped_raster_flags.h
index 5407b5e..e692d33 100644
--- a/cc/paint/scoped_raster_flags.h
+++ b/cc/paint/scoped_raster_flags.h
@@ -18,11 +18,13 @@
 class CC_PAINT_EXPORT ScopedRasterFlags {
  public:
   // |flags| and |image_provider| must outlive this class.
+  // |is_rasterizing| is true if these flags are for direct rasterization
+  // and false if these flags are being used for serialization.
   ScopedRasterFlags(const PaintFlags* flags,
                     ImageProvider* image_provider,
                     const SkMatrix& ctm,
                     uint8_t alpha,
-                    bool create_skia_shaders);
+                    bool is_rasterizing);
   ~ScopedRasterFlags();
 
   // The usage of these flags should not extend beyond the lifetime of this
@@ -36,7 +38,7 @@
 
  private:
   void DecodeImageShader(const SkMatrix& ctm);
-  void DecodeRecordShader(const SkMatrix& ctm, bool create_skia_shader);
+  void DecodeRecordShader(const SkMatrix& ctm, bool is_rasterizing);
   void DecodeFilter();
 
   void AdjustStrokeIfNeeded(const SkMatrix& ctm);
diff --git a/cc/paint/shader_transfer_cache_entry.cc b/cc/paint/shader_transfer_cache_entry.cc
new file mode 100644
index 0000000..5e66f1b6
--- /dev/null
+++ b/cc/paint/shader_transfer_cache_entry.cc
@@ -0,0 +1,31 @@
+// 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 "cc/paint/shader_transfer_cache_entry.h"
+
+namespace cc {
+
+ServiceShaderTransferCacheEntry::ServiceShaderTransferCacheEntry(
+    sk_sp<PaintShader> shader,
+    uint32_t raster_color_space_id,
+    size_t size)
+    : shader_(std::move(shader)),
+      raster_color_space_id_(raster_color_space_id),
+      size_(size) {}
+
+ServiceShaderTransferCacheEntry::~ServiceShaderTransferCacheEntry() = default;
+
+size_t ServiceShaderTransferCacheEntry::CachedSize() const {
+  return size_;
+}
+
+bool ServiceShaderTransferCacheEntry::Deserialize(
+    GrContext* context,
+    base::span<const uint8_t> data) {
+  // These entries must be created directly via CreateLocalEntry.
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace cc
diff --git a/cc/paint/shader_transfer_cache_entry.h b/cc/paint/shader_transfer_cache_entry.h
new file mode 100644
index 0000000..fcb6abb7
--- /dev/null
+++ b/cc/paint/shader_transfer_cache_entry.h
@@ -0,0 +1,46 @@
+// 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 CC_PAINT_SHADER_TRANSFER_CACHE_ENTRY_H_
+#define CC_PAINT_SHADER_TRANSFER_CACHE_ENTRY_H_
+
+#include "base/containers/span.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_shader.h"
+#include "cc/paint/transfer_cache_entry.h"
+
+namespace cc {
+
+// There is only a service transfer cache entry here.  The reason shaders
+// are cached at all are to reuse internal Skia caches for SkPictureShaders.
+// However, the major reason not to transfer from the client is that it
+// avoids the design change to make it possible for transfer cache entries
+// to depend on transfer cache entries.  This adds a number of wrinkles
+// (during serialization, deserialization, scheduling).  The assumption
+// is that most picture shaders are small (e.g. a few ops to draw a tiled
+// image) and that the design complication for this edge case isn't worth
+// it.
+
+class CC_PAINT_EXPORT ServiceShaderTransferCacheEntry
+    : public ServiceTransferCacheEntryBase<TransferCacheEntryType::kShader> {
+ public:
+  explicit ServiceShaderTransferCacheEntry(sk_sp<PaintShader> shader,
+                                           uint32_t raster_color_space_id,
+                                           size_t size);
+  ~ServiceShaderTransferCacheEntry() final;
+  size_t CachedSize() const final;
+  bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
+
+  sk_sp<PaintShader> shader() const { return shader_; }
+  uint32_t raster_color_space_id() const { return raster_color_space_id_; }
+
+ private:
+  sk_sp<PaintShader> shader_;
+  uint32_t raster_color_space_id_;
+  size_t size_ = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_SHADER_TRANSFER_CACHE_ENTRY_H_
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 83128d5c..686ed2b 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -16,7 +16,7 @@
 
 namespace cc {
 
-const bool SkiaPaintCanvas::kCreateSkiaShaders = true;
+const bool SkiaPaintCanvas::kIsRasterizing = true;
 
 SkiaPaintCanvas::ContextFlushes::ContextFlushes()
     : enable(false), max_draws_before_flush(-1) {}
@@ -170,9 +170,8 @@
                                SkScalar x1,
                                SkScalar y1,
                                const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -182,9 +181,8 @@
 }
 
 void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -194,9 +192,8 @@
 }
 
 void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -206,9 +203,8 @@
 }
 
 void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -218,9 +214,8 @@
 }
 
 void SkiaPaintCanvas::drawRRect(const SkRRect& rrect, const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -232,9 +227,8 @@
 void SkiaPaintCanvas::drawDRRect(const SkRRect& outer,
                                  const SkRRect& inner,
                                  const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -247,9 +241,8 @@
                                     SkScalar rx,
                                     SkScalar ry,
                                     const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -259,9 +252,8 @@
 }
 
 void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -277,7 +269,7 @@
   base::Optional<ScopedRasterFlags> scoped_flags;
   if (flags) {
     scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(),
-                         255u, kCreateSkiaShaders);
+                         255u, kIsRasterizing);
     if (!scoped_flags->flags())
       return;
   }
@@ -297,7 +289,7 @@
   base::Optional<ScopedRasterFlags> scoped_flags;
   if (flags) {
     scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(),
-                         255u, kCreateSkiaShaders);
+                         255u, kIsRasterizing);
     if (!scoped_flags->flags())
       return;
   }
@@ -317,7 +309,7 @@
   if (flags) {
     ScopedRasterFlags raster_flags(flags, image_provider_,
                                    canvas_->getTotalMatrix(), 255u,
-                                   kCreateSkiaShaders);
+                                   kIsRasterizing);
     if (!raster_flags.flags())
       return;
     SkPaint paint = raster_flags.flags()->ToSkPaint();
@@ -332,9 +324,8 @@
                                    SkScalar x,
                                    SkScalar y,
                                    const PaintFlags& flags) {
-  ScopedRasterFlags raster_flags(&flags, image_provider_,
-                                 canvas_->getTotalMatrix(), 255u,
-                                 kCreateSkiaShaders);
+  ScopedRasterFlags raster_flags(
+      &flags, image_provider_, canvas_->getTotalMatrix(), 255u, kIsRasterizing);
   if (!raster_flags.flags())
     return;
   SkPaint paint = raster_flags.flags()->ToSkPaint();
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index d5f39f6..502be1b6 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -143,7 +143,7 @@
 
  private:
   // We always need skia shaders since the ops will be played on an SkCanvas.
-  static const bool kCreateSkiaShaders;
+  static const bool kIsRasterizing;
 
   void WrapCanvasInColorSpaceXformCanvas(
       sk_sp<SkColorSpace> target_color_space);
diff --git a/cc/paint/transfer_cache_deserialize_helper.h b/cc/paint/transfer_cache_deserialize_helper.h
index 48d4c6f..530b479 100644
--- a/cc/paint/transfer_cache_deserialize_helper.h
+++ b/cc/paint/transfer_cache_deserialize_helper.h
@@ -32,16 +32,28 @@
     if (entry == nullptr) {
       return nullptr;
     }
+
+    total_size_ += entry->CachedSize();
+
     // The service side entry is created using T::kType, so the class created is
     // guaranteed to make the entry type.
     DCHECK_EQ(entry->Type(), entry_type);
     return static_cast<T*>(entry);
   }
 
+  // Creates an entry directly.  If an entry exists, it will be clobbered.
+  virtual void CreateLocalEntry(
+      uint32_t id,
+      std::unique_ptr<ServiceTransferCacheEntry> entry) = 0;
+
+  size_t GetTotalEntrySizes() const { return total_size_; }
+
  private:
   virtual ServiceTransferCacheEntry* GetEntryInternal(
       TransferCacheEntryType entry_type,
       uint32_t entry_id) = 0;
+
+  size_t total_size_ = 0;
 };
 
 };  // namespace cc
diff --git a/cc/paint/transfer_cache_entry.cc b/cc/paint/transfer_cache_entry.cc
index 66c9622..eb51292 100644
--- a/cc/paint/transfer_cache_entry.cc
+++ b/cc/paint/transfer_cache_entry.cc
@@ -12,6 +12,7 @@
 #include "cc/paint/paint_typeface_transfer_cache_entry.h"
 #include "cc/paint/path_transfer_cache_entry.h"
 #include "cc/paint/raw_memory_transfer_cache_entry.h"
+#include "cc/paint/shader_transfer_cache_entry.h"
 
 namespace cc {
 
@@ -28,6 +29,10 @@
       return std::make_unique<ServiceColorSpaceTransferCacheEntry>();
     case TransferCacheEntryType::kPath:
       return std::make_unique<ServicePathTransferCacheEntry>();
+    case TransferCacheEntryType::kShader:
+      // ServiceShaderTransferCache is only created via CreateLocalEntry
+      // and is never serialized/deserialized.
+      return nullptr;
   }
 
   return nullptr;
diff --git a/cc/paint/transfer_cache_entry.h b/cc/paint/transfer_cache_entry.h
index dca7b96e..474bd97 100644
--- a/cc/paint/transfer_cache_entry.h
+++ b/cc/paint/transfer_cache_entry.h
@@ -25,8 +25,9 @@
   kPaintTypeface,
   kColorSpace,
   kPath,
+  kShader,
   // Add new entries above this line, make sure to update kLast.
-  kLast = kPath,
+  kLast = kShader,
 };
 
 // An interface used on the client to serialize a transfer cache entry
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index bdef6b4f..33e8417f 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -735,7 +735,7 @@
 #if defined(OS_ANDROID)
       transfer_resource.is_backed_by_surface_texture =
           video_frame->metadata()->IsTrue(
-              media::VideoFrameMetadata::SURFACE_TEXTURE);
+              media::VideoFrameMetadata::TEXTURE_OWNER);
       transfer_resource.wants_promotion_hint = video_frame->metadata()->IsTrue(
           media::VideoFrameMetadata::WANTS_PROMOTION_HINT);
 #endif
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 994b1ce..1579df03 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -871,7 +871,7 @@
     const viz::LocalSurfaceId& local_surface_id) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   if (layer_tree_host_)
-    layer_tree_host_->SetLocalSurfaceId(local_surface_id);
+    layer_tree_host_->SetLocalSurfaceIdFromParent(local_surface_id);
 }
 
 void LayerTreeTest::DispatchSetDeferCommits(bool defer_commits) {
diff --git a/cc/test/transfer_cache_test_helper.cc b/cc/test/transfer_cache_test_helper.cc
index 073e6d8..8613c422 100644
--- a/cc/test/transfer_cache_test_helper.cc
+++ b/cc/test/transfer_cache_test_helper.cc
@@ -44,6 +44,18 @@
   EnforceLimits();
 }
 
+void TransferCacheTestHelper::CreateLocalEntry(
+    uint32_t id,
+    std::unique_ptr<ServiceTransferCacheEntry> entry) {
+  auto key = std::make_pair(entry->Type(), id);
+
+  DeleteEntryDirect(key);
+
+  entries_[key] = std::move(entry);
+  local_entries_.insert(key);
+  last_added_entry_ = key;
+}
+
 void TransferCacheTestHelper::UnlockEntriesDirect(
     const std::vector<EntryKey>& keys) {
   for (const auto& key : keys) {
@@ -54,6 +66,7 @@
 
 void TransferCacheTestHelper::DeleteEntryDirect(const EntryKey& key) {
   locked_entries_.erase(key);
+  local_entries_.erase(key);
   entries_.erase(key);
 }
 
@@ -70,15 +83,17 @@
     TransferCacheEntryType type,
     uint32_t id) {
   auto key = std::make_pair(type, id);
-  if (locked_entries_.count(key) == 0)
+  if (locked_entries_.count(key) + local_entries_.count(key) == 0)
     return nullptr;
-  DCHECK(entries_.find(key) != entries_.end());
+  if (entries_.find(key) == entries_.end())
+    return nullptr;
   return entries_[key].get();
 }
 
 bool TransferCacheTestHelper::LockEntryInternal(const EntryKey& key) {
   if (entries_.find(key) == entries_.end())
     return false;
+
   locked_entries_.insert(key);
   EnforceLimits();
   return true;
diff --git a/cc/test/transfer_cache_test_helper.h b/cc/test/transfer_cache_test_helper.h
index 399885d..fc86362 100644
--- a/cc/test/transfer_cache_test_helper.h
+++ b/cc/test/transfer_cache_test_helper.h
@@ -39,6 +39,10 @@
 
   const EntryKey& GetLastAddedEntry() const { return last_added_entry_; }
 
+  void CreateLocalEntry(
+      uint32_t id,
+      std::unique_ptr<ServiceTransferCacheEntry> entry) override;
+
  protected:
   // Serialization helpers.
   bool LockEntryInternal(const EntryKey& key) override;
@@ -50,6 +54,7 @@
   void EnforceLimits();
 
   std::map<EntryKey, std::unique_ptr<ServiceTransferCacheEntry>> entries_;
+  std::set<EntryKey> local_entries_;
   std::set<EntryKey> locked_entries_;
   EntryKey last_added_entry_ = {TransferCacheEntryType::kRawMemory, ~0};
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 7b90131..e3d9d41 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -433,7 +433,7 @@
 void LayerTreeHost::UpdateDeferCommitsInternal() {
   proxy_->SetDeferCommits(defer_commits_ ||
                           (settings_.enable_surface_synchronization &&
-                           !local_surface_id_.is_valid()));
+                           !local_surface_id_from_parent_.is_valid()));
 }
 
 bool LayerTreeHost::IsUsingLayerLists() const {
@@ -1052,8 +1052,8 @@
 void LayerTreeHost::SetViewportSizeAndScale(
     const gfx::Size& device_viewport_size,
     float device_scale_factor,
-    const viz::LocalSurfaceId& local_surface_id) {
-  SetLocalSurfaceId(local_surface_id);
+    const viz::LocalSurfaceId& local_surface_id_from_parent) {
+  SetLocalSurfaceIdFromParent(local_surface_id_from_parent);
 
   bool changed = false;
   if (device_viewport_size_ != device_viewport_size) {
@@ -1080,7 +1080,8 @@
 #if defined(OS_MACOSX)
     // TODO(ccameron): This check is not valid on Aura or Mus yet, but should
     // be.
-    CHECK(!has_pushed_local_surface_id_ || !local_surface_id_.is_valid());
+    CHECK(!has_pushed_local_surface_id_from_parent_ ||
+          !local_surface_id_from_parent_.is_valid());
 #endif
   }
 }
@@ -1177,12 +1178,12 @@
   SetNeedsCommit();
 }
 
-void LayerTreeHost::SetLocalSurfaceId(
-    const viz::LocalSurfaceId& local_surface_id) {
-  if (local_surface_id_ == local_surface_id)
+void LayerTreeHost::SetLocalSurfaceIdFromParent(
+    const viz::LocalSurfaceId& local_surface_id_from_parent) {
+  if (local_surface_id_from_parent_ == local_surface_id_from_parent)
     return;
-  local_surface_id_ = local_surface_id;
-  has_pushed_local_surface_id_ = false;
+  local_surface_id_from_parent_ = local_surface_id_from_parent;
+  has_pushed_local_surface_id_from_parent_ = false;
   UpdateDeferCommitsInternal();
   SetNeedsCommit();
 }
@@ -1372,8 +1373,8 @@
 
   tree_impl->set_content_source_id(content_source_id_);
 
-  tree_impl->SetLocalSurfaceId(local_surface_id_);
-  has_pushed_local_surface_id_ = true;
+  tree_impl->SetLocalSurfaceIdFromParent(local_surface_id_from_parent_);
+  has_pushed_local_surface_id_from_parent_ = true;
 
   if (pending_page_scale_animation_) {
     tree_impl->SetPendingPageScaleAnimation(
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 9673b70..7954f931 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -292,9 +292,10 @@
     return event_listener_properties_[static_cast<size_t>(event_class)];
   }
 
-  void SetViewportSizeAndScale(const gfx::Size& device_viewport_size,
-                               float device_scale_factor,
-                               const viz::LocalSurfaceId& local_surface_id);
+  void SetViewportSizeAndScale(
+      const gfx::Size& device_viewport_size,
+      float device_scale_factor,
+      const viz::LocalSurfaceId& local_surface_id_from_parent);
 
   void SetViewportVisibleRect(const gfx::Rect& visible_rect);
 
@@ -337,9 +338,10 @@
 
   // If this LayerTreeHost needs a valid viz::LocalSurfaceId then commits will
   // be deferred until a valid viz::LocalSurfaceId is provided.
-  void SetLocalSurfaceId(const viz::LocalSurfaceId& local_surface_id);
-  const viz::LocalSurfaceId& local_surface_id() const {
-    return local_surface_id_;
+  void SetLocalSurfaceIdFromParent(
+      const viz::LocalSurfaceId& local_surface_id_from_parent);
+  const viz::LocalSurfaceId& local_surface_id_from_parent() const {
+    return local_surface_id_from_parent_;
   }
 
   void SetRasterColorSpace(const gfx::ColorSpace& raster_color_space);
@@ -636,9 +638,9 @@
   gfx::ColorSpace raster_color_space_;
 
   uint32_t content_source_id_;
-  viz::LocalSurfaceId local_surface_id_;
+  viz::LocalSurfaceId local_surface_id_from_parent_;
   // Used to detect surface invariant violations.
-  bool has_pushed_local_surface_id_ = false;
+  bool has_pushed_local_surface_id_from_parent_ = false;
   bool defer_commits_ = false;
 
   SkColor background_color_ = SK_ColorWHITE;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a8772a5c3..86208aee 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -929,12 +929,14 @@
 
   // If we have a new LocalSurfaceId, we must always submit a CompositorFrame
   // because the parent is blocking on us.
-  bool local_surface_id_changed =
-      last_draw_local_surface_id_ != active_tree->local_surface_id();
+  bool local_surface_id_from_parent_changed =
+      last_draw_local_surface_id_ !=
+      active_tree->local_surface_id_from_parent();
 
   return root_surface_has_visible_damage ||
          active_tree_->property_trees()->effect_tree.HasCopyRequests() ||
-         must_always_swap || hud_wants_to_draw_ || local_surface_id_changed;
+         must_always_swap || hud_wants_to_draw_ ||
+         local_surface_id_from_parent_changed;
 }
 
 DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) {
@@ -1895,8 +1897,8 @@
   metadata.device_scale_factor = active_tree_->painted_device_scale_factor() *
                                  active_tree_->device_scale_factor();
   metadata.viewport_size_in_pixels = device_viewport_size();
-  if (active_tree()->local_surface_id().is_valid())
-    metadata.local_surface_id = active_tree()->local_surface_id();
+  if (active_tree()->local_surface_id_from_parent().is_valid())
+    metadata.local_surface_id = active_tree()->local_surface_id_from_parent();
 
   active_tree_->GetViewportSelection(&metadata.selection);
   metadata.is_mobile_optimized = IsMobileOptimized(active_tree_.get());
@@ -2028,11 +2030,11 @@
     // LocalSurfaceId might slip through, but single-thread-without-scheduler
     // mode is only used in tests so it doesn't matter.
     CHECK(!settings_.single_thread_proxy_scheduler ||
-          active_tree()->local_surface_id().is_valid());
+          active_tree()->local_surface_id_from_parent().is_valid());
     layer_tree_frame_sink_->SetLocalSurfaceId(
-        active_tree()->local_surface_id());
+        active_tree()->local_surface_id_from_parent());
   }
-  last_draw_local_surface_id_ = active_tree()->local_surface_id();
+  last_draw_local_surface_id_ = active_tree()->local_surface_id_from_parent();
   if (const char* client_name = GetClientNameForMetrics()) {
     size_t total_quad_count = 0;
     for (const auto& pass : compositor_frame.render_pass_list)
@@ -3187,9 +3189,15 @@
   scroll_status.main_thread_scrolling_reasons =
       MainThreadScrollingReason::kNotScrollingOnMain;
   if (!scrolling_node) {
-    scroll_status.thread = SCROLL_IGNORED;
-    if (settings_.is_layer_tree_for_subframe)
+    if (settings_.is_layer_tree_for_subframe) {
+      TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)",
+                           TRACE_EVENT_SCOPE_THREAD);
       scroll_status.thread = SCROLL_UNKNOWN;
+    } else {
+      TRACE_EVENT_INSTANT0("cc", "Ignroed - No ScrollNode",
+                           TRACE_EVENT_SCOPE_THREAD);
+      scroll_status.thread = SCROLL_IGNORED;
+    }
     scroll_status.main_thread_scrolling_reasons =
         MainThreadScrollingReason::kNoScrollingLayer;
     return scroll_status;
@@ -3211,6 +3219,8 @@
   // If the CurrentlyScrollingNode doesn't exist after distributing scroll
   // delta, no scroller can scroll in the given delta hint direction(s).
   if (!active_tree_->CurrentlyScrollingNode()) {
+    TRACE_EVENT_INSTANT0("cc", "Ignored - Didnt Scroll",
+                         TRACE_EVENT_SCOPE_THREAD);
     scroll_status.thread = InputHandler::SCROLL_IGNORED;
     scroll_status.main_thread_scrolling_reasons =
         MainThreadScrollingReason::kNotScrollingOnMain;
@@ -3270,6 +3280,7 @@
 
     if (layer_impl) {
       if (!IsInitialScrollHitTestReliable(layer_impl, device_viewport_point)) {
+        TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
         scroll_status.thread = SCROLL_UNKNOWN;
         scroll_status.main_thread_scrolling_reasons =
             MainThreadScrollingReason::kFailedHitTest;
@@ -3336,6 +3347,7 @@
 
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimatedBegin(
     ScrollState* scroll_state) {
+  TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollAnimatedBegin");
   InputHandler::ScrollStatus scroll_status;
   scroll_status.main_thread_scrolling_reasons =
       MainThreadScrollingReason::kNotScrollingOnMain;
@@ -3347,6 +3359,8 @@
     if (ScrollAnimationUpdateTarget(scroll_node, delta, base::TimeDelta())) {
       scroll_status.thread = SCROLL_ON_IMPL_THREAD;
     } else {
+      TRACE_EVENT_INSTANT0("cc", "Failed to create animation",
+                           TRACE_EVENT_SCOPE_THREAD);
       scroll_status.thread = SCROLL_IGNORED;
       scroll_status.main_thread_scrolling_reasons =
           MainThreadScrollingReason::kNotScrollable;
@@ -3436,6 +3450,7 @@
     const gfx::Point& viewport_point,
     const gfx::Vector2dF& scroll_delta,
     base::TimeDelta delayed_by) {
+  TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollAnimated");
   InputHandler::ScrollStatus scroll_status;
   scroll_status.main_thread_scrolling_reasons =
       MainThreadScrollingReason::kNotScrollingOnMain;
@@ -3462,6 +3477,8 @@
     if (ScrollAnimationUpdateTarget(scroll_node, delta, delayed_by)) {
       scroll_status.thread = SCROLL_ON_IMPL_THREAD;
     } else {
+      TRACE_EVENT_INSTANT0("cc", "Failed to update animation",
+                           TRACE_EVENT_SCOPE_THREAD);
       scroll_status.thread = SCROLL_IGNORED;
       scroll_status.main_thread_scrolling_reasons =
           MainThreadScrollingReason::kNotScrollable;
@@ -3991,6 +4008,9 @@
 
 void LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset(
     const gfx::ScrollOffset& root_offset) {
+  TRACE_EVENT2("cc",
+               "LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset",
+               "offset_x", root_offset.x(), "offset_y", root_offset.y());
   bool changed = active_tree_->DistributeRootScrollOffset(root_offset);
   if (!changed)
     return;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index a4afbc7..f97dd57 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -180,7 +180,7 @@
   }
   void DidActivateSyncTree() override {
     // Make sure the active tree always has a valid LocalSurfaceId.
-    host_impl_->active_tree()->SetLocalSurfaceId(
+    host_impl_->active_tree()->SetLocalSurfaceIdFromParent(
         viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u)));
   }
   void WillPrepareTiles() override {}
@@ -242,7 +242,7 @@
     bool init = host_impl_->InitializeRenderer(layer_tree_frame_sink_.get());
     host_impl_->SetViewportSize(gfx::Size(10, 10));
     host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
-    host_impl_->active_tree()->SetLocalSurfaceId(
+    host_impl_->active_tree()->SetLocalSurfaceIdFromParent(
         viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u)));
     // Set the viz::BeginFrameArgs so that methods which use it are able to.
     host_impl_->WillBeginImplFrame(viz::CreateBeginFrameArgsForTesting(
@@ -3532,7 +3532,7 @@
     host_impl_->active_tree()->BuildPropertyTreesForTesting();
     host_impl_->active_tree()->DidBecomeActive();
     host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain();
-    host_impl_->active_tree()->SetLocalSurfaceId(
+    host_impl_->active_tree()->SetLocalSurfaceIdFromParent(
         viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u)));
     DrawFrame();
 
@@ -9081,7 +9081,7 @@
   root->test_properties()->AddChild(std::move(child));
   layer_tree_host_impl->active_tree()->SetRootLayerForTesting(std::move(root));
   layer_tree_host_impl->active_tree()->BuildPropertyTreesForTesting();
-  layer_tree_host_impl->active_tree()->SetLocalSurfaceId(
+  layer_tree_host_impl->active_tree()->SetLocalSurfaceIdFromParent(
       viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u)));
 
   TestFrameData frame;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 34cc3a3..e35e5eb8 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2242,7 +2242,7 @@
         // a second commit as a result.
         layer_tree_host()->SetViewportSizeAndScale(
             layer_tree_host()->device_viewport_size(), 4.f,
-            layer_tree_host()->local_surface_id());
+            layer_tree_host()->local_surface_id_from_parent());
         break;
       default:
         // No extra commits.
@@ -2287,7 +2287,7 @@
     if (layer_tree_host()->SourceFrameNumber() == 1) {
       layer_tree_host()->SetViewportSizeAndScale(
           layer_tree_host()->device_viewport_size(), 4.f,
-          layer_tree_host()->local_surface_id());
+          layer_tree_host()->local_surface_id_from_parent());
     }
   }
 
@@ -8012,7 +8012,7 @@
                                    DrawResult draw_result) override {
     EXPECT_EQ(DRAW_SUCCESS, draw_result);
     EXPECT_EQ(expected_local_surface_id_,
-              host_impl->active_tree()->local_surface_id());
+              host_impl->active_tree()->local_surface_id_from_parent());
     return draw_result;
   }
 
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 1da7d9f..642763b 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -469,7 +469,7 @@
 
   target_tree->set_content_source_id(content_source_id());
 
-  target_tree->SetLocalSurfaceId(local_surface_id());
+  target_tree->SetLocalSurfaceIdFromParent(local_surface_id_from_parent());
 
   target_tree->pending_page_scale_animation_ =
       std::move(pending_page_scale_animation_);
@@ -963,11 +963,11 @@
   host_impl_->SetNeedUpdateGpuRasterizationStatus();
 }
 
-void LayerTreeImpl::SetLocalSurfaceId(
-    const viz::LocalSurfaceId& local_surface_id) {
+void LayerTreeImpl::SetLocalSurfaceIdFromParent(
+    const viz::LocalSurfaceId& local_surface_id_from_parent) {
   CHECK(!settings().enable_surface_synchronization || !viewport_size_invalid_ ||
-        local_surface_id_ != local_surface_id);
-  local_surface_id_ = local_surface_id;
+        local_surface_id_from_parent_ != local_surface_id_from_parent);
+  local_surface_id_from_parent_ = local_surface_id_from_parent;
 }
 
 void LayerTreeImpl::SetRasterColorSpace(
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index de485a9..f2fac6a5 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -298,9 +298,9 @@
   void set_content_source_id(uint32_t id) { content_source_id_ = id; }
   uint32_t content_source_id() { return content_source_id_; }
 
-  void SetLocalSurfaceId(const viz::LocalSurfaceId& id);
-  const viz::LocalSurfaceId& local_surface_id() const {
-    return local_surface_id_;
+  void SetLocalSurfaceIdFromParent(const viz::LocalSurfaceId& id);
+  const viz::LocalSurfaceId& local_surface_id_from_parent() const {
+    return local_surface_id_from_parent_;
   }
 
   void SetRasterColorSpace(int raster_color_space_id,
@@ -605,7 +605,7 @@
   gfx::ColorSpace raster_color_space_;
 
   uint32_t content_source_id_;
-  viz::LocalSurfaceId local_surface_id_;
+  viz::LocalSurfaceId local_surface_id_from_parent_;
 
   scoped_refptr<SyncedElasticOverscroll> elastic_overscroll_;
 
diff --git a/chrome/VERSION b/chrome/VERSION
index be6f264..a8f5a7e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=68
 MINOR=0
-BUILD=3425
+BUILD=3426
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 7b7ceed8..72621a5 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -858,7 +858,6 @@
     ":chrome_jni_for_test_registration($default_toolchain)",
     "//base/test:test_support",
     "//components/heap_profiling:test_support",
-    "//content/public/test/android:content_native_test_support",
   ]
 }
 
@@ -1124,7 +1123,6 @@
   }
   deps = [
     "//components/heap_profiling:heap_profiling_java_test_support",
-    "//content/public/test/android:content_java_test_support",
   ]
 }
 
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index b83bd56..93dae039 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -57,10 +57,13 @@
       "*ic_file_download_white*",  # Bottom edge seems misaligned.
       "*ic_lock.*",  # Bottom edge seems misaligned.
     ]
-    _native_lib_file =
-        rebase_path("$root_gen_dir/CHROME_VERSION.json", root_out_dir)
-    native_lib_version_arg = "@FileArg($_native_lib_file:full-quoted)"
-    native_lib_version_rule = "//build/util:chrome_version_json"
+
+    if (defined(shared_libraries) && shared_libraries != []) {
+      _native_lib_file =
+          rebase_path("$root_gen_dir/CHROME_VERSION.json", root_out_dir)
+      native_lib_version_arg = "@FileArg($_native_lib_file:full-quoted)"
+      native_lib_version_rule = "//build/util:chrome_version_json"
+    }
     if (!defined(aapt_locale_whitelist)) {
       aapt_locale_whitelist = locales - android_chrome_omitted_locales
     }
diff --git a/chrome/android/java/res/anim/fast_out_slow_in_interpolator.xml b/chrome/android/java/res/anim/fast_out_slow_in_interpolator.xml
new file mode 100644
index 0000000..043aa21
--- /dev/null
+++ b/chrome/android/java/res/anim/fast_out_slow_in_interpolator.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="0.2"
+    android:controlY2="1"
+    tools:targetApi="21"/>
diff --git a/chrome/android/java/res/drawable-hdpi/btn_settings.png b/chrome/android/java/res/drawable-hdpi/btn_settings.png
new file mode 100644
index 0000000..110af6e
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/btn_settings.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/btn_settings.png b/chrome/android/java/res/drawable-mdpi/btn_settings.png
new file mode 100644
index 0000000..4512431
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/btn_settings.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/btn_settings.png b/chrome/android/java/res/drawable-xhdpi/btn_settings.png
new file mode 100644
index 0000000..0584aaaf
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/btn_settings.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/btn_settings.png b/chrome/android/java/res/drawable-xxhdpi/btn_settings.png
new file mode 100644
index 0000000..ba6e4803
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/btn_settings.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/btn_settings.png b/chrome/android/java/res/drawable-xxxhdpi/btn_settings.png
new file mode 100644
index 0000000..786191c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/btn_settings.png
Binary files differ
diff --git a/chrome/android/java/res/drawable/signin_header_animation.xml b/chrome/android/java/res/drawable/signin_header_animation.xml
index badebed..459d307f 100644
--- a/chrome/android/java/res/drawable/signin_header_animation.xml
+++ b/chrome/android/java/res/drawable/signin_header_animation.xml
@@ -135,17 +135,18 @@
         </vector>
     </aapt:attr>
     <!-- Each animation here has a period of 10.45 seconds. Animation is restarted from the code. -->
-    <!-- Light grey circle -->
     <target android:name="cloud1_animation_group">
         <aapt:attr name="android:animation">
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="3900"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
                     android:valueTo="-3.24"/>
                 <objectAnimator
                     android:duration="6550"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="-3.24"
                     android:valueTo="0"/>
@@ -157,11 +158,13 @@
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="4650"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
                     android:valueTo="-2.16"/>
                 <objectAnimator
                     android:duration="5800"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="-2.16"
                     android:valueTo="0"/>
@@ -173,11 +176,13 @@
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="6150"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
                     android:valueTo="-5.04"/>
                 <objectAnimator
                     android:duration="4300"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="-5.04"
                     android:valueTo="0"/>
@@ -189,11 +194,13 @@
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="5550"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
                     android:valueTo="5.04"/>
                 <objectAnimator
                     android:duration="4900"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="5.04"
                     android:valueTo="0"/>
@@ -205,23 +212,27 @@
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:valueFrom="0"
                     android:valueTo="-360"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:startOffset="1900"
                     android:valueFrom="0"
                     android:valueTo="-360"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:startOffset="1900"
                     android:valueFrom="0"
                     android:valueTo="-360"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:startOffset="1900"
                     android:valueFrom="0"
@@ -234,6 +245,7 @@
             <set android:ordering="sequentially">
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:valueFrom="-120"
                     android:valueTo="120"/>
@@ -244,6 +256,7 @@
                     android:valueTo="120"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:valueFrom="120"
                     android:valueTo="-120"/>
@@ -254,6 +267,7 @@
                     android:valueTo="-120"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:valueFrom="-120"
                     android:valueTo="120"/>
@@ -264,6 +278,7 @@
                     android:valueTo="120"/>
                 <objectAnimator
                     android:duration="700"
+                    android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="rotation"
                     android:valueFrom="120"
                     android:valueTo="-120"/>
diff --git a/chrome/android/java/res/menu/download_manager_menu.xml b/chrome/android/java/res/menu/download_manager_menu.xml
index 8292553e..962c956 100644
--- a/chrome/android/java/res/menu/download_manager_menu.xml
+++ b/chrome/android/java/res/menu/download_manager_menu.xml
@@ -29,29 +29,18 @@
     <group android:id="@+id/with_settings_normal_menu_group"
         android:visible="false" >
         <item
+            android:id="@+id/settings_menu_id"
+            android:icon="@drawable/btn_settings"
+            android:title="@string/preferences"
+            android:visible="false"
+            chrome:showAsAction="ifRoom" />
+        <item
             android:id="@+id/with_settings_search_menu_id"
             android:icon="@drawable/ic_search"
             android:title="@string/search"
             android:visible="false"
             chrome:showAsAction="ifRoom" />
         <item
-            android:id="@+id/extra_menu_id"
-            android:icon="@drawable/ic_more_vert_black_24dp"
-            android:title=""
-            chrome:showAsAction="ifRoom" >
-            <menu>
-                <item
-                    android:id="@+id/with_settings_info_menu_id"
-                    android:title="@string/download_manager_ui_show_storage"
-                    android:visible="false"
-                    chrome:showAsAction="never" />
-                <item
-                    android:id="@+id/settings_menu_id"
-                    android:title="@string/preferences"
-                    chrome:showAsAction="never" />
-            </menu>
-        </item>
-        <item
             android:id="@+id/with_settings_close_menu_id"
             android:icon="@drawable/btn_close"
             android:title="@string/close"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index dcd923d..a398a8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -166,8 +166,6 @@
     public static final String CHROME_HOME = "ChromeHome";
     public static final String CHROME_HOME_DROP_ALL_BUT_FIRST_THUMBNAIL =
             "ChromeHomeDropAllButFirstThumbnail";
-    public static final String CHROME_HOME_MENU_ITEMS_EXPAND_SHEET =
-            "ChromeHomeMenuItemsExpandSheet";
     public static final String CHROME_HOME_PERSISTENT_IPH = "ChromeHomePersistentIph";
     public static final String CHROME_HOME_PULL_TO_REFRESH_IPH_AT_TOP =
             "ChromeHomePullToRefreshIphAtTop";
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 a91a42c1..527b081 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -25,7 +25,6 @@
 import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -1499,23 +1498,6 @@
 
             @Override
             public View getHeaderView() {
-                // Return early if Chrome Home is not enabled.
-                if (getBottomSheet() == null) return null;
-
-                boolean isPageMenu = getAppMenuPropertiesDelegate().shouldShowPageMenu();
-
-                // Return early if the conditions aren't right to show the Chrome Home IPH menu
-                // header.
-                if (mControlContainer.getVisibility() != View.VISIBLE
-                        || getBottomSheet().isSheetOpen() || !isPageMenu
-                        || AppMenuPropertiesDelegate.shouldShowNavMenuItems()) {
-                    return null;
-                }
-
-                LayoutInflater inflater = LayoutInflater.from(ChromeTabbedActivity.this);
-                Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
-
-                // Default is no header.
                 return null;
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
index f28b13f..a0072fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser;
 
 import android.os.Bundle;
+import android.support.annotation.CallSuper;
 import android.support.v7.app.AppCompatActivity;
 
 import org.chromium.base.Log;
@@ -22,6 +23,7 @@
 public abstract class SynchronousInitializationActivity extends AppCompatActivity {
     private static final String TAG = "SyncInitActivity";
 
+    @CallSuper
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
index 63cd081..73d1e970 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -20,11 +20,9 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.StrictModeContext;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.UrlConstants;
@@ -33,12 +31,10 @@
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.webapk.lib.client.WebApkValidator;
@@ -49,24 +45,6 @@
  * App Menu helper that handles hiding and showing menu items based on activity state.
  */
 public class AppMenuPropertiesDelegate {
-    /**
-     * The param name for the "ChromeHomeMenuItemsExpandSheet" experiment. This specifies the number
-     * of times a menu item can be tapped before being hidden.
-     */
-    private static final String CHROME_HOME_MENU_ITEM_TAP_PARAM_NAME = "max_taps";
-
-    /**
-     * The number of times that bookmarks, downloads, and history can be triggered from the overflow
-     * menu in Chrome Home before they are hidden.
-     */
-    private static final int CHROME_HOME_MENU_ITEM_TAP_MAX = 10;
-
-    /**
-     * Whether or not the Chrome Home menu items should be hidden because they have been tapped the
-     * maximum number of times.
-     */
-    private static boolean sHideChromeHomeMenuItems;
-
     protected MenuItem mReloadMenuItem;
 
     protected final ChromeActivity mActivity;
@@ -221,13 +199,6 @@
             // Only display the Enter VR button if VR Shell Dev environment is enabled.
             menu.findItem(R.id.enter_vr_id).setVisible(
                     CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_VR_SHELL_DEV));
-
-            if (!shouldShowNavMenuItems()) {
-                // History, downloads, and bookmarks are shown in the Chrome Home bottom sheet.
-                menu.findItem(R.id.open_history_menu_id).setVisible(false);
-                menu.findItem(R.id.downloads_menu_id).setVisible(false);
-                menu.findItem(R.id.all_bookmarks_menu_id).setVisible(false);
-            }
         }
 
         if (isOverviewMenu) {
@@ -441,32 +412,4 @@
                         ? mActivity.getString(R.string.menu_request_desktop_site_on)
                         : mActivity.getString(R.string.menu_request_desktop_site_off));
     }
-
-    /**
-     * @return Whether bookmarks, downloads, and history should be shown in the menu.
-     */
-    public static boolean shouldShowNavMenuItems() {
-        if (!FeatureUtilities.isChromeHomeEnabled()) return true;
-
-        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
-            int maxTapCount = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
-                    ChromeFeatureList.CHROME_HOME_MENU_ITEMS_EXPAND_SHEET,
-                    CHROME_HOME_MENU_ITEM_TAP_PARAM_NAME, CHROME_HOME_MENU_ITEM_TAP_MAX);
-
-            sHideChromeHomeMenuItems = sHideChromeHomeMenuItems
-                    || ChromePreferenceManager.getInstance().getChromeHomeMenuItemClickCount()
-                            >= maxTapCount;
-        }
-
-        boolean chromeHomeMenuItemFlagEnabled = ChromeFeatureList.isInitialized()
-                && ChromeFeatureList.isEnabled(
-                           ChromeFeatureList.CHROME_HOME_MENU_ITEMS_EXPAND_SHEET);
-
-        // If Chrome Home or the menu item feature is disabled, clear the menu tap preference.
-        if (!chromeHomeMenuItemFlagEnabled) {
-            ChromePreferenceManager.getInstance().clearChromeHomeMenuItemClickCount();
-        }
-
-        return chromeHomeMenuItemFlagEnabled && !sHideChromeHomeMenuItems;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
index 1060101..03068d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
@@ -318,7 +318,7 @@
     }
 
     @CalledByNative
-    private void didSwapBuffers() {
+    private void didSwapBuffers(boolean swappedCurrentSize) {
         // If we're in the middle of a surface swap, then see if we've received a new frame yet for
         // the new surface before hiding the outgoing surface.
         if (mFramesUntilHideBackground > 1) {
@@ -333,7 +333,10 @@
             mCompositorSurfaceManager.doneWithUnownedSurface();
         }
 
-        runDrawFinishedCallbacks();
+        // Only run our draw finished callbacks if the frame we swapped was the correct size.
+        if (swappedCurrentSize) {
+            runDrawFinishedCallbacks();
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index 0d5443f..53a5475 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -11,10 +11,10 @@
 import android.support.annotation.IntDef;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
@@ -38,10 +38,8 @@
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.resources.ResourceManager;
 
 import java.io.Serializable;
@@ -89,9 +87,6 @@
     private static final int FLING_MIN_DURATION = 100; // ms
 
     private static final float THRESHOLD_TO_SWITCH_STACK = 0.4f;
-    private static final int NEW_TAB_ANIMATION_DURATION_MS = 300;
-
-    public static final int MODERN_TOP_MARGIN_DP = 16;
 
     /**
      * The delta time applied on the velocity from the fling. This is to compute the kick to help
@@ -99,6 +94,12 @@
      */
     private static final float SWITCH_STACK_FLING_DT = 1.0f / 30.0f;
 
+    /**
+     * True if this is currently the active layout and startHiding() has not yet been called, false
+     * otherwise.
+     */
+    protected boolean mIsActiveLayout;
+
     /** The list of potentially visible stacks. */
     protected final ArrayList<Stack> mStacks;
 
@@ -151,6 +152,9 @@
     private static final int LAYOUTTAB_ASYNCHRONOUS_INITIALIZATION_BATCH_SIZE = 4;
     private boolean mDelayedLayoutTabInitRequired;
 
+    /** Which model (normal or incognito) was active when StackLayout was shown. */
+    private int mModelIndexWhenOpened;
+
     /**
      * Temporarily stores the index of the selected tab stack. This is used to set the currently
      * selected stack in TabModelSelector once the stack-switching animation finishes.
@@ -168,15 +172,6 @@
 
     private StackLayoutGestureHandler mGestureHandler;
 
-    /** A {@link LayoutTab} used for new tab animations. */
-    private LayoutTab mNewTabLayoutTab;
-
-    /**
-     * Whether or not the new layout tab has been properly initialized (a frame can occur between
-     * creation and initialization).
-     */
-    private boolean mIsNewTabInitialized;
-
     private class StackLayoutGestureHandler implements GestureHandler {
         @Override
         public void onDown(float x, float y, boolean fromMouse, int buttons) {
@@ -441,6 +436,22 @@
 
     @Override
     public void onTabSelecting(long time, int tabId) {
+        // We update TabModelSelector's current model when incognito mode is toggled in the tab
+        // switcher. So the "current model index" is already the one that we're leaving active when
+        // the tab switcher is closed.
+        final int newModelIndex = mTabModelSelector.getCurrentModelIndex();
+        if (newModelIndex != mModelIndexWhenOpened) {
+            final int indexInNewModel = mTabModelSelector.getCurrentModel().index();
+            if (indexInNewModel == mTabModelSelector.getCurrentModel().index()) {
+                // TabModelImpl logs this action when we switch to a different index within a
+                // TabModelImpl. If we switch between TabModelImpls (i.e. switch between normal and
+                // incognito mode), but leave the index the same (i.e. switch back to the most
+                // recently active tab in that stack), TabModelImpl doesn't catch that case, so we
+                // log it here.
+                RecordUserAction.record("MobileTabSwitched");
+            }
+        }
+
         commitOutstandingModelState(time);
         if (tabId == Tab.INVALID_TAB_ID) tabId = mTabModelSelector.getCurrentTabId();
         super.onTabSelecting(time, tabId);
@@ -535,23 +546,14 @@
         startHiding(id, false);
         mStacks.get(getTabStackIndex(id)).tabCreated(time, id);
 
-        if (FeatureUtilities.isChromeHomeEnabled()) {
-            mNewTabLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BUTTON, NO_TITLE);
-            mNewTabLayoutTab.setScale(1.f);
-            mNewTabLayoutTab.setBorderScale(1.f);
-            mNewTabLayoutTab.setDecorationAlpha(0.f);
-            mNewTabLayoutTab.setY(getHeight() / 2);
-
-            mIsNewTabInitialized = true;
-
-            Interpolator interpolator = BakedBezierInterpolator.TRANSFORM_CURVE;
-            addToAnimation(mNewTabLayoutTab, LayoutTab.Property.Y, mNewTabLayoutTab.getY(), 0.f,
-                    NEW_TAB_ANIMATION_DURATION_MS, 0, false, interpolator);
-        } else {
-            startMarginAnimation(false);
-        }
+        startMarginAnimation(false);
     }
 
+    // This method is called if the following sequence of operations occurs:
+    // 1. Enter multi-window mode
+    // 2. Create a second Chrome instance by moving a tab to the other window
+    // 3. In the top window, enter the tab switcher
+    // 4. Expand the top window to full screen.
     @Override
     public void onTabRestored(long time, int tabId) {
         super.onTabRestored(time, tabId);
@@ -593,10 +595,6 @@
     @Override
     protected void onAnimationFinished() {
         if (mStackAnimationCount == 0) super.onAnimationFinished();
-        if (mNewTabLayoutTab != null) {
-            mIsNewTabInitialized = false;
-            mNewTabLayoutTab = null;
-        }
     }
 
     /**
@@ -693,6 +691,21 @@
     public void show(long time, boolean animate) {
         super.show(time, animate);
 
+        if (!mIsActiveLayout) {
+            // The mIsActiveLayout check is necessary because there are certain edge cases where
+            // show() is called (e.g. to refresh the Stacks) while the tab switcher is already
+            // showing.
+
+            // Note: there are some edge cases (e.g. the last open tab is closed somehow while the
+            // tab switcher is not open) that can also cause this event to be logged without a
+            // toolbar interaction. The event name contains "Toolbar" for historical reasons; the
+            // current intent is to log whenever the tab switcher is entered.
+            RecordUserAction.record("MobileToolbarShowStackView");
+
+            mModelIndexWhenOpened = mTabModelSelector.getCurrentModelIndex();
+        }
+        mIsActiveLayout = true;
+
         Tab tab = mTabModelSelector.getCurrentTab();
         if (tab != null && tab.isNativePage()) mTabContentManager.cacheTabThumbnail(tab);
 
@@ -942,7 +955,6 @@
         }
 
         float getTopHeightOffset() {
-            if (FeatureUtilities.isChromeHomeEnabled()) return MODERN_TOP_MARGIN_DP;
             return getTopBrowserControlsHeight() * mStackOffsetYPercent;
         }
     }
@@ -1147,12 +1159,10 @@
             tabVisibleCount += mStacks.get(i).getVisibleCount();
         }
 
-        int layoutTabCount = tabVisibleCount + (mNewTabLayoutTab == null ? 0 : 1);
-
-        if (layoutTabCount == 0) {
+        if (tabVisibleCount == 0) {
             mLayoutTabs = null;
-        } else if (mLayoutTabs == null || mLayoutTabs.length != layoutTabCount) {
-            mLayoutTabs = new LayoutTab[layoutTabCount];
+        } else if (mLayoutTabs == null || mLayoutTabs.length != tabVisibleCount) {
+            mLayoutTabs = new LayoutTab[tabVisibleCount];
         }
 
         int index = 0;
@@ -1169,11 +1179,6 @@
             if (mLayoutTabs[i].updateSnap(dt)) needUpdate = true;
         }
 
-        if (mNewTabLayoutTab != null && mIsNewTabInitialized) {
-            mLayoutTabs[mLayoutTabs.length - 1] = mNewTabLayoutTab;
-            if (mNewTabLayoutTab.updateSnap(dt)) needUpdate = true;
-        }
-
         if (needUpdate) requestUpdate();
 
         // Since we've updated the positions of the stacks and tabs, let's go ahead and update
@@ -1219,6 +1224,15 @@
     }
 
     @Override
+    public void startHiding(int nextTabId, boolean hintAtTabSelection) {
+        // Reset mIsActiveLayout here instead of in doneHiding() so if a user hits the tab switcher
+        // button on the toolbar to re-open it while we're still in the process of hiding the tab
+        // switcher, we don't skip the logging.
+        super.startHiding(nextTabId, hintAtTabSelection);
+        mIsActiveLayout = false;
+    }
+
+    @Override
     public void doneHiding() {
         super.doneHiding();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
index 4baef34e..69eee007 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
 
@@ -404,12 +403,10 @@
      * @param id The id of the new tab to animate.
      */
     public void tabCreated(long time, int id) {
-        if (!FeatureUtilities.isChromeHomeEnabled()) {
-            if (!createTabHelper(id)) return;
-            mIsDying = false;
+        if (!createTabHelper(id)) return;
+        mIsDying = false;
 
-            finishAnimation(time);
-        }
+        finishAnimation(time);
         startAnimation(time, OverviewAnimationType.NEW_TAB_OPENED,
                 TabModelUtils.getTabIndexById(mTabList, id), TabList.INVALID_TAB_INDEX, false);
     }
@@ -2008,7 +2005,7 @@
                 LayoutTab layoutTab = mLayout.createLayoutTab(tabId, isIncognito,
                         Layout.SHOW_CLOSE_BUTTON, needTitle, maxContentWidth, maxContentHeight);
                 layoutTab.setInsetBorderVertical(true);
-                layoutTab.setShowToolbar(!FeatureUtilities.isChromeHomeEnabled());
+                layoutTab.setShowToolbar(true);
                 layoutTab.setToolbarAlpha(0.f);
                 layoutTab.setAnonymizeToolbar(!mIsStackForCurrentTabList || mTabList.index() != i);
 
@@ -2280,9 +2277,6 @@
      * @return The maximum height of a layout tab in the tab switcher.
      */
     public float getMaxTabHeight() {
-        if (FeatureUtilities.isChromeHomeEnabled() && mCurrentMode == Orientation.PORTRAIT) {
-            return mLayout.getHeightMinusBrowserControls() - StackLayoutBase.MODERN_TOP_MARGIN_DP;
-        }
         return mLayout.getHeightMinusBrowserControls();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
index f46f95c..35dcab7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
@@ -21,7 +21,6 @@
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
 
@@ -129,11 +128,8 @@
                         set, tab, SCALE, tab.getScale(), 1.0f, TAB_FOCUSED_ANIMATION_DURATION, 0);
                 addAnimation(set, tab, X_IN_STACK_INFLUENCE, tab.getXInStackInfluence(), 0.0f,
                         TAB_FOCUSED_ANIMATION_DURATION, 0);
-                int tabYInfluenceDuration = FeatureUtilities.isChromeHomeEnabled()
-                        ? TAB_FOCUSED_ANIMATION_DURATION
-                        : TAB_FOCUSED_Y_STACK_DURATION;
                 addAnimation(set, tab, Y_IN_STACK_INFLUENCE, tab.getYInStackInfluence(), 0.0f,
-                        tabYInfluenceDuration, 0);
+                        TAB_FOCUSED_Y_STACK_DURATION, 0);
 
                 addAnimation(set, tab.getLayoutTab(), MAX_CONTENT_HEIGHT,
                         tab.getLayoutTab().getMaxContentHeight(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
index 3475350..b12c318d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
 
@@ -137,11 +136,8 @@
                         TAB_FOCUSED_ANIMATION_DURATION, 0);
                 addAnimation(
                         set, tab, SCALE, tab.getScale(), 1.0f, TAB_FOCUSED_ANIMATION_DURATION, 0);
-                int tabYInfluenceDuration = FeatureUtilities.isChromeHomeEnabled()
-                        ? TAB_FOCUSED_ANIMATION_DURATION
-                        : TAB_FOCUSED_Y_STACK_DURATION;
                 addAnimation(set, tab, Y_IN_STACK_INFLUENCE, tab.getYInStackInfluence(), 0.0f,
-                        tabYInfluenceDuration, 0);
+                        TAB_FOCUSED_Y_STACK_DURATION, 0);
                 addAnimation(set, tab.getLayoutTab(), MAX_CONTENT_HEIGHT,
                         tab.getLayoutTab().getMaxContentHeight(),
                         tab.getLayoutTab().getUnclampedOriginalContentHeight(),
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 5fb023e..201aa37 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
@@ -192,11 +192,7 @@
 
     @Override
     public void onSizeChanged(
-            float width, float height, float visibleViewportOffsetY, int orientation) {
-        // If Chrome Home is enabled, a size change means the toolbar is now in a different
-        // location so a render is needed.
-        if (FeatureUtilities.isChromeHomeEnabled()) mRenderHost.requestRender();
-    }
+            float width, float height, float visibleViewportOffsetY, int orientation) {}
 
     @Override
     public void getVirtualViews(List<VirtualView> views) {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerToolbar.java
index d69e6da9..b8420b12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerToolbar.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.support.v7.widget.AppCompatSpinner;
 import android.util.AttributeSet;
+import android.view.MenuItem;
 import android.view.View;
 import android.widget.Spinner;
 
@@ -97,9 +98,8 @@
     @Override
     protected void onDataChanged(int numItems) {
         super.onDataChanged(numItems);
-        getMenu()
-                .findItem(mInfoMenuItemId)
-                .setVisible(!mIsSearching && !mIsSelectionEnabled && numItems > 0);
+        MenuItem item = getMenu().findItem(mInfoMenuItemId);
+        if (item != null) item.setVisible(!mIsSearching && !mIsSelectionEnabled && numItems > 0);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index d39659e3..7fc7459 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -7,6 +7,8 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.support.annotation.IntDef;
@@ -20,6 +22,7 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.DiscardableReferencePool;
@@ -222,7 +225,7 @@
      *                           activity than the main Chrome activity.
      * @param snackbarManager The {@link SnackbarManager} used to display snackbars.
      */
-    @SuppressWarnings("unchecked") // mSelectableListLayout
+    @SuppressWarnings({"unchecked"}) // mSelectableListLayout
     public DownloadManagerUi(Activity activity, boolean isOffTheRecord,
             ComponentName parentComponent, boolean isSeparateActivity,
             SnackbarManager snackbarManager) {
@@ -266,7 +269,7 @@
         int normalGroupId =
                 isLocationEnabled ? R.id.with_settings_normal_menu_group : R.id.normal_menu_group;
         mSearchMenuId = isLocationEnabled ? R.id.with_settings_search_menu_id : R.id.search_menu_id;
-        mInfoMenuId = isLocationEnabled ? R.id.with_settings_info_menu_id : R.id.info_menu_id;
+        mInfoMenuId = isLocationEnabled ? 0 : R.id.info_menu_id;
 
         mToolbar = (DownloadManagerToolbar) mSelectableListLayout.initializeToolbar(
                 R.layout.download_manager_toolbar, mBackendProvider.getSelectionDelegate(), 0, null,
@@ -282,10 +285,11 @@
         addObserver(mToolbar);
 
         if (isLocationEnabled) {
-            mToolbar.setExtraMenuItem(R.id.extra_menu_id);
-            mToolbar.setInfoButtonText(R.string.download_manager_ui_show_storage,
-                    R.string.download_manager_ui_hide_storage);
-            mToolbar.setShowInfoIcon(false);
+            // TODO(xingliu): Use the new settings icon with better alpha value.
+            Drawable settingIcon = mToolbar.getMenu().findItem(R.id.settings_menu_id).getIcon();
+            settingIcon.setColorFilter(ApiCompatibilityUtils.getColor(getActivity().getResources(),
+                                               R.color.black_alpha_65),
+                    PorterDuff.Mode.SRC_IN);
             final Tracker tracker =
                     TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
             tracker.addOnInitializedCallback(
@@ -568,7 +572,7 @@
         if (!tracker.shouldTriggerHelpUI(FeatureConstants.DOWNLOAD_SETTINGS_FEATURE)) return;
 
         // Build and show text bubble.
-        View anchorView = mToolbar.findViewById(R.id.extra_menu_id);
+        View anchorView = mToolbar.findViewById(R.id.settings_menu_id);
         TextBubble textBubble =
                 new TextBubble(mActivity, (View) mToolbar, R.string.iph_download_settings_text,
                         R.string.iph_download_settings_accessibility_text,
@@ -583,7 +587,7 @@
     }
 
     private void toggleHighlightForDownloadSettingsTextBubble(boolean shouldHighlight) {
-        View view = mToolbar.findViewById(R.id.extra_menu_id);
+        View view = mToolbar.findViewById(R.id.settings_menu_id);
 
         if (shouldHighlight) {
             ViewHighlighter.turnOnHighlight(view, true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
index c3d0f87..7823930 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/FramebustBlockInfoBar.java
@@ -10,6 +10,7 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
@@ -20,6 +21,9 @@
  * continue the redirection by tapping on a link.
  */
 public class FramebustBlockInfoBar extends InfoBar {
+    /** For Log statements. */
+    private static final String TAG = "Framebust Infobar";
+
     private final String mBlockedUrl;
 
     /** Whether the infobar should be shown as a mini-infobar or a classic expanded one. */
@@ -42,6 +46,9 @@
         layout.setMessage(getString(R.string.redirect_blocked_message));
         InfoBarControlLayout control = layout.addControlLayout();
 
+        // TODO(crbug.com/834959): remove after bug fixed.
+        Log.i(TAG, "Mark possible occurance of crbug.com/834959");
+
         ViewGroup ellipsizerView =
                 (ViewGroup) LayoutInflater.from(getContext())
                         .inflate(R.layout.infobar_control_url_ellipsizer, control, false);
@@ -61,7 +68,8 @@
         ellipsizerView.setOnClickListener(view -> onLinkClicked());
 
         control.addView(ellipsizerView);
-        layout.setButtons(getContext().getResources().getString(R.string.got_it), null);
+        layout.setButtons(
+                getContext().getResources().getString(R.string.always_allow_redirects), null);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
index 774aff6..5a21cb2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
@@ -12,7 +12,7 @@
 import android.widget.RadioGroup.OnCheckedChangeListener;
 
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.widget.RadioButtonLayout;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 8e8c0a97..c15fcaff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -26,8 +26,8 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.SearchEnginePreference;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
index 33ba2a2..cc8cc22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilder.java
@@ -22,6 +22,7 @@
 import android.widget.RemoteViews;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
@@ -77,6 +78,9 @@
      */
     private static final int BUTTON_ICON_COLOR_MATERIAL = 0xff757575;
 
+    /** For Log statements. */
+    private static final String TAG = "Custom Notification";
+
     private final Context mContext;
 
     public CustomNotificationBuilder(Context context) {
@@ -116,6 +120,9 @@
             StrictMode.setThreadPolicy(oldPolicy);
         }
 
+        // TODO(crbug.com/834959): remove after bug fixed.
+        Log.i(TAG, "Mark possible occurrence of crbug.com/834959");
+
         for (RemoteViews view : new RemoteViews[] {compactView, bigView}) {
             view.setTextViewText(R.id.time, formattedTime);
             view.setTextViewText(R.id.title, mTitle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 706ce4b..21289a71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -70,8 +70,6 @@
 
     public static final String CHROME_HOME_INFO_PROMO_SHOWN_KEY = "chrome_home_info_promo_shown";
 
-    private static final String CHROME_HOME_MENU_ITEM_CLICK_COUNT_KEY =
-            "chrome_home_menu_item_click_count";
     private static final String SOLE_INTEGRATION_ENABLED_KEY = "sole_integration_enabled";
 
     private static final String COMMAND_LINE_ON_NON_ROOTED_ENABLED_KEY =
@@ -454,30 +452,6 @@
         removeKey(CHROME_HOME_OPT_OUT_SNACKBAR_SHOWN);
     }
 
-    /**
-     * @return The number of times that bookmarks, history, or downloads have been triggered from
-     *         the overflow menu while Chrome Home is enabled.
-     */
-    public int getChromeHomeMenuItemClickCount() {
-        return readInt(CHROME_HOME_MENU_ITEM_CLICK_COUNT_KEY);
-    }
-
-    /**
-     * Increment the count for the number of times bookmarks, history, or downloads have been
-     * triggered from the overflow menu while Chrome Home is enabled.
-     */
-    public void incrementChromeHomeMenuItemClickCount() {
-        writeInt(CHROME_HOME_MENU_ITEM_CLICK_COUNT_KEY, getChromeHomeMenuItemClickCount() + 1);
-    }
-
-    /**
-     * Remove the count for number of times bookmarks, history, or downloads were clicked while
-     * Chrome Home is enabled.
-     */
-    public void clearChromeHomeMenuItemClickCount() {
-        mSharedPreferences.edit().remove(CHROME_HOME_MENU_ITEM_CLICK_COUNT_KEY).apply();
-    }
-
     /** Marks that the content suggestions surface has been shown. */
     public void setSuggestionsSurfaceShown() {
         writeBoolean(CONTENT_SUGGESTIONS_SHOWN_KEY, true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index 4470899..1366f2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -21,8 +21,8 @@
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.base.DeviceFormFactor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
index 6cc2c9b..66dc51fe6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
@@ -9,6 +9,7 @@
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.IntDef;
 import android.support.annotation.StringRes;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -36,14 +37,20 @@
 import org.chromium.chrome.browser.preferences.website.NotificationInfo;
 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.components.location.LocationUtils;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
 * A custom adapter for listing search engines.
@@ -57,6 +64,20 @@
     private static final int VIEW_TYPE_DIVIDER = 1;
     private static final int VIEW_TYPE_COUNT = 2;
 
+    public static final int MAX_RECENT_ENGINE_NUM = 3;
+    public static final long MAX_DISPLAY_TIME_SPAN_MS = TimeUnit.DAYS.toMillis(2);
+
+    /**
+     * Type for source of search engine. This is needed because if a custom search engine is set as
+     * default, it will be moved to the prepopulated list.
+     */
+    @IntDef({TYPE_DEFAULT, TYPE_PREPOPULATED, TYPE_RECENT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TemplateUrlSourceType {}
+    public static final int TYPE_DEFAULT = 0;
+    public static final int TYPE_PREPOPULATED = 1;
+    public static final int TYPE_RECENT = 2;
+
     /** The current context. */
     private Context mContext;
 
@@ -139,7 +160,10 @@
             return;  // Flow continues in onTemplateUrlServiceLoaded below.
         }
 
-        List<TemplateUrl> templateUrls = templateUrlService.getSearchEngines();
+        List<TemplateUrl> templateUrls = templateUrlService.getTemplateUrls();
+        TemplateUrl defaultSearchEngineTemplateUrl =
+                templateUrlService.getDefaultSearchEngineTemplateUrl();
+        sortAndFilterUnnecessaryTemplateUrl(templateUrls, defaultSearchEngineTemplateUrl);
         boolean forceRefresh = mIsLocationPermissionChanged;
         mIsLocationPermissionChanged = false;
         if (!didSearchEnginesChange(templateUrls)) {
@@ -152,27 +176,24 @@
 
         for (int i = 0; i < templateUrls.size(); i++) {
             TemplateUrl templateUrl = templateUrls.get(i);
-            if (templateUrl.getType() == TemplateUrlService.TYPE_PREPOPULATED
-                    || templateUrl.getType() == TemplateUrlService.TYPE_DEFAULT) {
-                mPrepopulatedSearchEngines.add(templateUrl);
-            } else {
+            if (getSearchEngineSourceType(templateUrl, defaultSearchEngineTemplateUrl)
+                    == TYPE_RECENT) {
                 mRecentSearchEngines.add(templateUrl);
+            } else {
+                mPrepopulatedSearchEngines.add(templateUrl);
             }
         }
 
-        int defaultSearchEngineIndex =
-                TemplateUrlService.getInstance().getDefaultSearchEngineIndex();
-
         // Convert the TemplateUrl index into an index of mSearchEngines.
         mSelectedSearchEnginePosition = -1;
         for (int i = 0; i < mPrepopulatedSearchEngines.size(); ++i) {
-            if (mPrepopulatedSearchEngines.get(i).getIndex() == defaultSearchEngineIndex) {
+            if (mPrepopulatedSearchEngines.get(i).equals(defaultSearchEngineTemplateUrl)) {
                 mSelectedSearchEnginePosition = i;
             }
         }
 
         for (int i = 0; i < mRecentSearchEngines.size(); ++i) {
-            if (mRecentSearchEngines.get(i).getIndex() == defaultSearchEngineIndex) {
+            if (mRecentSearchEngines.get(i).equals(defaultSearchEngineTemplateUrl)) {
                 // Add one to offset the title for the recent search engine list.
                 mSelectedSearchEnginePosition = i + computeStartIndexForRecentSearchEngines();
             }
@@ -188,11 +209,62 @@
         notifyDataSetChanged();
     }
 
+    public static void sortAndFilterUnnecessaryTemplateUrl(
+            List<TemplateUrl> templateUrls, TemplateUrl defaultSearchEngine) {
+        Collections.sort(templateUrls, new Comparator<TemplateUrl>() {
+            @Override
+            public int compare(TemplateUrl templateUrl1, TemplateUrl templateUrl2) {
+                if (templateUrl1.getIsPrepopulated() && templateUrl2.getIsPrepopulated()) {
+                    return templateUrl1.getPrepopulatedId() - templateUrl2.getPrepopulatedId();
+                } else if (templateUrl1.getIsPrepopulated()) {
+                    return -1;
+                } else if (templateUrl2.getIsPrepopulated()) {
+                    return 1;
+                } else if (templateUrl1.equals(templateUrl2)) {
+                    return 0;
+                } else if (templateUrl1.equals(defaultSearchEngine)) {
+                    return -1;
+                } else if (templateUrl2.equals(defaultSearchEngine)) {
+                    return 1;
+                } else {
+                    return ApiCompatibilityUtils.compareLong(
+                            templateUrl2.getLastVisitedTime(), templateUrl1.getLastVisitedTime());
+                }
+            }
+        });
+        int recentEngineNum = 0;
+        long displayTime = System.currentTimeMillis() - MAX_DISPLAY_TIME_SPAN_MS;
+        Iterator<TemplateUrl> iterator = templateUrls.iterator();
+        while (iterator.hasNext()) {
+            TemplateUrl templateUrl = iterator.next();
+            if (getSearchEngineSourceType(templateUrl, defaultSearchEngine) != TYPE_RECENT) {
+                continue;
+            }
+            if (recentEngineNum < MAX_RECENT_ENGINE_NUM
+                    && templateUrl.getLastVisitedTime() > displayTime) {
+                recentEngineNum++;
+            } else {
+                iterator.remove();
+            }
+        }
+    }
+
+    private static @TemplateUrlSourceType int getSearchEngineSourceType(
+            TemplateUrl templateUrl, TemplateUrl defaultSearchEngine) {
+        if (templateUrl.getIsPrepopulated()) {
+            return TYPE_PREPOPULATED;
+        } else if (templateUrl.equals(defaultSearchEngine)) {
+            return TYPE_DEFAULT;
+        } else {
+            return TYPE_RECENT;
+        }
+    }
+
     private static boolean containsTemplateUrl(
             List<TemplateUrl> templateUrls, TemplateUrl targetTemplateUrl) {
         for (int i = 0; i < templateUrls.size(); i++) {
             TemplateUrl templateUrl = templateUrls.get(i);
-            // Explicitly excluding TemplateUrlType and Index as they might change if a search
+            // Explicitly excluding TemplateUrlSourceType and Index as they might change if a search
             // engine is set as default.
             if (templateUrl.getIsPrepopulated() == targetTemplateUrl.getIsPrepopulated()
                     && TextUtils.equals(templateUrl.getKeyword(), targetTemplateUrl.getKeyword())
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
index 6d22897..daa86443 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentSettingsResources.java
@@ -151,9 +151,9 @@
                                  R.string.website_settings_category_notifications_ask, 0));
             localMap.put(ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS,
                     new ResourceItem(R.drawable.permission_popups, R.string.popup_permission_title,
-                                 R.string.popup_permission_title, ContentSetting.ALLOW,
-                                 ContentSetting.BLOCK, 0,
-                                 R.string.website_settings_category_popups_blocked));
+                            R.string.popup_permission_title, ContentSetting.ALLOW,
+                            ContentSetting.BLOCK, 0,
+                            R.string.website_settings_category_popups_redirects_blocked));
             localMap.put(ContentSettingsType.CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
                     new ResourceItem(R.drawable.permission_protected_media,
                                  org.chromium.chrome.R.string.protected_content,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrl.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrl.java
new file mode 100644
index 0000000..f3bd8673
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrl.java
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.chrome.browser.search_engines;
+
+import org.chromium.base.annotations.CalledByNative;
+
+import java.util.Locale;
+
+/**
+ * Represents object of a search engine. It only caches the native pointer of TemplateURL object
+ * from native side. Any class uses this need to register a {@link TemplateUrlServiceObserver} on
+ * {@link TemplatUrlService} to listen the native changes in case the native pointer is destroyed.
+ */
+public class TemplateUrl {
+    private final long mTemplateUrlPtr;
+
+    @CalledByNative
+    private static TemplateUrl create(long templateUrlPtr) {
+        return new TemplateUrl(templateUrlPtr);
+    }
+
+    protected TemplateUrl(long templateUrlPtr) {
+        mTemplateUrlPtr = templateUrlPtr;
+    }
+
+    /**
+     * @return The name of the search engine.
+     */
+    public String getShortName() {
+        return nativeGetShortName(mTemplateUrlPtr);
+    }
+
+    /**
+     * @return The prepopulated id of the search engine. For predefined engines, this field is a
+     *         non-zero, for custom search engines, it will return 0.
+     */
+    public int getPrepopulatedId() {
+        return nativeGetPrepopulatedId(mTemplateUrlPtr);
+    }
+
+    /**
+     * @return Whether a search engine is prepopulated or created by policy.
+     */
+    public boolean getIsPrepopulated() {
+        return nativeIsPrepopulatedOrCreatedByPolicy(mTemplateUrlPtr);
+    }
+
+    /**
+     * @return The keyword of the search engine.
+     */
+    public String getKeyword() {
+        return nativeGetKeyword(mTemplateUrlPtr);
+    }
+
+    /**
+     * @return The last time used this search engine. If a search engine hasn't been used, it will
+     *         return 0.
+     */
+    public long getLastVisitedTime() {
+        return nativeGetLastVisitedTime(mTemplateUrlPtr);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof TemplateUrl)) return false;
+        TemplateUrl otherTemplateUrl = (TemplateUrl) other;
+        return mTemplateUrlPtr == otherTemplateUrl.mTemplateUrlPtr;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US,
+                "TemplateURL -- keyword: %s, short name: %s, "
+                        + "prepopulated: %b",
+                getKeyword(), getShortName(), getIsPrepopulated());
+    }
+
+    private static native String nativeGetShortName(long templateUrlPtr);
+    private static native String nativeGetKeyword(long templateUrlPtr);
+    private static native boolean nativeIsPrepopulatedOrCreatedByPolicy(long templateUrlPtr);
+    private static native long nativeGetLastVisitedTime(long templateUrlPtr);
+    private static native int nativeGetPrepopulatedId(long templateUrlPtr);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
index df1817ed..21b19d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
@@ -4,21 +4,15 @@
 
 package org.chromium.chrome.browser.search_engines;
 
-import android.support.annotation.IntDef;
-import android.text.TextUtils;
+import android.support.annotation.Nullable;
 
 import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Locale;
-
-import javax.annotation.Nullable;
 
 /**
  * Android wrapper of the TemplateUrlService which provides access from the Java
@@ -48,95 +42,6 @@
         void onTemplateURLServiceChanged();
     }
 
-    /**
-     * Type for default search engine which is not prepopulated. This is needed because
-     * if a custom search engine is set as default, it will be moved to the prepopulated list.
-     */
-    @IntDef({TYPE_DEFAULT, TYPE_PREPOPULATED, TYPE_RECENT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TemplateUrlType {}
-    public static final int TYPE_DEFAULT = 0;
-    public static final int TYPE_PREPOPULATED = 1;
-    public static final int TYPE_RECENT = 2;
-
-    /**
-     * Represents search engine with its index.
-     */
-    public static class TemplateUrl {
-        private final int mIndex;
-        private final String mShortName;
-        private final boolean mIsPrepopulated;
-        private final String mKeyword;
-        @TemplateUrlType private int mTemplateUrlType;
-
-        @CalledByNative("TemplateUrl")
-        public static TemplateUrl create(
-                int index, String shortName, boolean isPrepopulated, String keyword) {
-            return new TemplateUrl(index, shortName, isPrepopulated, keyword);
-        }
-
-        public TemplateUrl(
-                int index, String shortName, boolean isPrepopulated, String keyword) {
-            mIndex = index;
-            mShortName = shortName;
-            mIsPrepopulated = isPrepopulated;
-            mKeyword = keyword;
-        }
-
-        public int getIndex() {
-            return mIndex;
-        }
-
-        public String getShortName() {
-            return mShortName;
-        }
-
-        public boolean getIsPrepopulated() {
-            return mIsPrepopulated;
-        }
-
-        public String getKeyword() {
-            return mKeyword;
-        }
-
-        public void setType(@TemplateUrlType int templateUrlType) {
-            mTemplateUrlType = templateUrlType;
-        }
-
-        @TemplateUrlType
-        public int getType() {
-            return mTemplateUrlType;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + mIndex;
-            result = prime * result + ((mShortName == null) ? 0 : mShortName.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (!(other instanceof TemplateUrl)) return false;
-            TemplateUrl otherTemplateUrl = (TemplateUrl) other;
-            return mIndex == otherTemplateUrl.mIndex
-                    && mTemplateUrlType == otherTemplateUrl.mTemplateUrlType
-                    && mIsPrepopulated == otherTemplateUrl.mIsPrepopulated
-                    && TextUtils.equals(mKeyword, otherTemplateUrl.mKeyword)
-                    && TextUtils.equals(mShortName, otherTemplateUrl.mShortName);
-        }
-
-        @Override
-        public String toString() {
-            return String.format(Locale.US,
-                    "TemplateURL -- keyword: %s, short name: %s, index: %d, "
-                    + "type: %d, prepopulated: %b",
-                    mKeyword, mShortName, mIndex, mTemplateUrlType, mIsPrepopulated);
-        }
-    }
-
     private static TemplateUrlService sService;
 
     public static TemplateUrlService getInstance() {
@@ -194,47 +99,24 @@
     }
 
     /**
-     * Sets whether filtering of the search engines is enabled.  Filtering ensures the list of
-     * search engines is appropriate for displaying in settings, but can cause issues if you expect
-     * the list of search engines returned here to match the underlying TemplateUrlService in
-     * native.
-     *
-     * @param enableFiltering Whether to enable filtering.
+     * Returns a list of the all available search engines.
      */
-    public void setFilteringEnabled(boolean enableFiltering) {
-        nativeSetFilteringEnabled(mNativeTemplateUrlServiceAndroid, enableFiltering);
-    }
-
-    /**
-     * Returns a list of the prepopulated search engines.
-     *
-     * Warning: TemplateUrl.getIndex() is *not* an index into this list, since this list contains
-     * only prepopulated search engines. E.g. getLocalizedSearchEngines().get(0).getIndex() could
-     * return 3.
-     */
-    public List<TemplateUrl> getSearchEngines() {
+    public List<TemplateUrl> getTemplateUrls() {
         ThreadUtils.assertOnUiThread();
-        int defaultSearchEngineIndex = getDefaultSearchEngineIndex();
-        int templateUrlCount = nativeGetTemplateUrlCount(mNativeTemplateUrlServiceAndroid);
-        List<TemplateUrl> templateUrls = new ArrayList<TemplateUrl>(templateUrlCount);
-        for (int i = 0; i < templateUrlCount; i++) {
-            TemplateUrl templateUrl = nativeGetTemplateUrlAt(mNativeTemplateUrlServiceAndroid, i);
-            if (templateUrl != null) {
-                setSearchEngineType(templateUrl, defaultSearchEngineIndex);
-                templateUrls.add(templateUrl);
-            }
-        }
+        List<TemplateUrl> templateUrls = new ArrayList<>();
+        nativeGetTemplateUrls(mNativeTemplateUrlServiceAndroid, templateUrls);
         return templateUrls;
     }
 
-    private void setSearchEngineType(TemplateUrl templateUrl, int defaultSearchEngineIndex) {
-        if (templateUrl.getIsPrepopulated()) {
-            templateUrl.setType(TYPE_PREPOPULATED);
-        } else if (templateUrl.getIndex() == defaultSearchEngineIndex) {
-            templateUrl.setType(TYPE_DEFAULT);
-        } else {
-            templateUrl.setType(TYPE_RECENT);
-        }
+    /**
+     * Called from native to populate the list of all available search engines.
+     * @param templateUrls The list of {@link TemplateUrl} to be added.
+     * @param templateUrl The {@link TemplateUrl} would add to the list.
+     */
+    @CalledByNative
+    private static void addTemplateUrlToList(
+            List<TemplateUrl> templateUrls, TemplateUrl templateUrl) {
+        templateUrls.add(templateUrl);
     }
 
     /**
@@ -256,28 +138,12 @@
     }
 
     /**
-     * @return The default search engine index (e.g., 0, 1, 2,...).
-     */
-    public int getDefaultSearchEngineIndex() {
-        ThreadUtils.assertOnUiThread();
-        return nativeGetDefaultSearchProviderIndex(mNativeTemplateUrlServiceAndroid);
-    }
-
-    /**
-     * @return {@link TemplateUrlService.TemplateUrl} for the default search engine.  This can
+     * @return {@link TemplateUrl} for the default search engine.  This can
      *         be null if DSEs are disabled entirely by administrators.
      */
     public @Nullable TemplateUrl getDefaultSearchEngineTemplateUrl() {
         if (!isLoaded()) return null;
-
-        int defaultSearchEngineIndex = getDefaultSearchEngineIndex();
-        if (defaultSearchEngineIndex == -1) return null;
-
-        assert defaultSearchEngineIndex >= 0;
-        assert defaultSearchEngineIndex < nativeGetTemplateUrlCount(
-                mNativeTemplateUrlServiceAndroid);
-
-        return nativeGetTemplateUrlAt(mNativeTemplateUrlServiceAndroid, defaultSearchEngineIndex);
+        return nativeGetDefaultSearchEngine(mNativeTemplateUrlServiceAndroid);
     }
 
     public void setSearchEngine(String selectedKeyword) {
@@ -458,13 +324,8 @@
     private native long nativeInit();
     private native void nativeLoad(long nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsLoaded(long nativeTemplateUrlServiceAndroid);
-    private native void nativeSetFilteringEnabled(
-            long nativeTemplateUrlServiceAndroid, boolean disableFiltering);
-    private native int nativeGetTemplateUrlCount(long nativeTemplateUrlServiceAndroid);
-    private native TemplateUrl nativeGetTemplateUrlAt(long nativeTemplateUrlServiceAndroid, int i);
     private native void nativeSetUserSelectedDefaultSearchProvider(
             long nativeTemplateUrlServiceAndroid, String selectedKeyword);
-    private native int nativeGetDefaultSearchProviderIndex(long nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsDefaultSearchManaged(long nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsSearchResultsPageFromDefaultSearchProvider(
             long nativeTemplateUrlServiceAndroid, String url);
@@ -488,4 +349,7 @@
             long nativeTemplateUrlServiceAndroid, String keyword);
     private native String nativeExtractSearchTermsFromUrl(
             long nativeTemplateUrlServiceAndroid, String url);
+    private native void nativeGetTemplateUrls(
+            long nativeTemplateUrlServiceAndroid, List<TemplateUrl> templateUrls);
+    private native TemplateUrl nativeGetDefaultSearchEngine(long nativeTemplateUrlServiceAndroid);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index cf56df04..d543745 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -32,9 +32,9 @@
 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
 import org.chromium.chrome.browser.util.IntentUtils;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninActivity.java
index 14ea392d..ac98911 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninActivity.java
@@ -9,15 +9,12 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.IntDef;
-import android.support.v7.app.AppCompatActivity;
 
-import org.chromium.base.Log;
-import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
@@ -31,7 +28,7 @@
  * An Activity displayed from the MainPreferences to allow the user to pick an account to
  * sign in to. The AccountSigninView.Delegate interface is fulfilled by the AppCompatActivity.
  */
-public class AccountSigninActivity extends AppCompatActivity
+public class AccountSigninActivity extends SynchronousInitializationActivity
         implements AccountSigninView.Listener, AccountSigninView.Delegate {
     private static final String TAG = "AccountSigninActivity";
     private static final String INTENT_IS_FROM_PERSONALIZED_PROMO =
@@ -144,17 +141,6 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        // The browser process must be started here because this activity may be started from the
-        // recent apps list and it relies on other activities and the native library to be loaded.
-        try {
-            ChromeBrowserInitializer.getInstance(this).handleSynchronousStartup();
-        } catch (ProcessInitException e) {
-            Log.e(TAG, "Failed to start browser process.", e);
-            // Since the library failed to initialize nothing in the application
-            // can work, so kill the whole application not just the activity
-            System.exit(-1);
-        }
-
         // We don't trust android to restore the saved state correctly, so pass null.
         super.onCreate(null);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivity.java
index 7bd8e2c..08a09a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivity.java
@@ -9,18 +9,15 @@
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
-import android.support.v7.app.AppCompatActivity;
 
-import org.chromium.base.Log;
-import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.SynchronousInitializationActivity;
 
 /**
  * Allows user to pick an account and sign in. Started from Settings and various sign-in promos.
  */
 // TODO(https://crbug.com/820491): extend AsyncInitializationActivity.
-public class SigninActivity extends AppCompatActivity {
+public class SigninActivity extends SynchronousInitializationActivity {
     private static final String TAG = "SigninActivity";
 
     /**
@@ -37,17 +34,6 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        // The browser process must be started here because this activity may be started from the
-        // recent apps list and it relies on other activities and the native library to be loaded.
-        try {
-            ChromeBrowserInitializer.getInstance(this).handleSynchronousStartup();
-        } catch (ProcessInitException e) {
-            Log.e(TAG, "Failed to start browser process.", e);
-            // Since the library failed to initialize nothing in the application
-            // can work, so kill the whole application not just the activity
-            System.exit(-1);
-        }
-
         super.onCreate(savedInstanceState);
         setContentView(R.layout.signin_activity);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index e093bf9f..d04d0a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -57,8 +57,8 @@
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager.HomepageStateListener;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index f407ac6b..108a2c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -592,7 +592,6 @@
             dismissTabSwitcherCallout();
             cancelAppMenuUpdateBadgeAnimation();
             mTabSwitcherListener.onClick(mToggleTabStackButton);
-            RecordUserAction.record("MobileToolbarShowStackView");
         }
     }
 
@@ -2701,4 +2700,3 @@
         }
     }
 }
-
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index a5e8b41..3058f71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -47,9 +47,12 @@
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.vr_shell.keyboard.VrInputMethodManagerWrapper;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
 import org.chromium.chrome.browser.widget.newtab.NewTabButton;
@@ -85,6 +88,7 @@
     private final boolean mVrBrowsingEnabled;
 
     private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
+    private TabModelSelectorTabModelObserver mTabModelSelectorTabModelObserver;
 
     private long mNativeVrShell;
 
@@ -373,7 +377,8 @@
         swapToTab(tab);
         createTabList();
         mActivity.getTabModelSelector().addObserver(mTabModelSelectorObserver);
-        createTabModelSelectorTabObserver();
+        attachTabModelSelectorTabObserver();
+        attachTabModelSelectorTabModelObserver();
         updateHistoryButtonsVisibility();
 
         mPresentationView.setOnTouchListener(mTouchListener);
@@ -705,6 +710,7 @@
         }
         mTabModelSelector.removeObserver(mTabModelSelectorObserver);
         mTabModelSelectorTabObserver.destroy();
+        mTabModelSelectorTabModelObserver.destroy();
         if (mTab != null) {
             mTab.removeObserver(mTabObserver);
             restoreTabFromVR();
@@ -903,7 +909,8 @@
         if (mNativeVrShell != 0) nativeSetSurface(mNativeVrShell, null);
     }
 
-    private void createTabModelSelectorTabObserver() {
+    /** Creates and attaches a TabModelSelectorTabObserver to the tab model selector. */
+    private void attachTabModelSelectorTabObserver() {
         assert mTabModelSelectorTabObserver == null;
         mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelector) {
             @Override
@@ -931,6 +938,18 @@
         };
     }
 
+    /** Creates and attaches a TabModelSelectorTabModelObserver to the tab model selector. */
+    private void attachTabModelSelectorTabModelObserver() {
+        assert mTabModelSelectorTabModelObserver == null;
+        mTabModelSelectorTabModelObserver =
+                new TabModelSelectorTabModelObserver(mTabModelSelector) {
+                    @Override
+                    public void didSelectTab(Tab tab, TabSelectionType type, int lastId) {
+                        nativeOnTabSelected(mNativeVrShell, tab.isIncognito(), tab.getId());
+                    }
+                };
+    }
+
     @CalledByNative
     public boolean hasDaydreamSupport() {
         return mDelegate.hasDaydreamSupport();
@@ -1000,6 +1019,15 @@
     }
 
     @CalledByNative
+    public void selectTab(int id, boolean incognito) {
+        TabModel tabModel = mTabModelSelector.getModel(incognito);
+        int index = TabModelUtils.getTabIndexById(tabModel, id);
+        if (index != TabModel.INVALID_TAB_INDEX) {
+            tabModel.setIndex(index, TabSelectionType.FROM_USER);
+        }
+    }
+
+    @CalledByNative
     public void openBookmarks() {
         mActivity.onMenuOrKeyboardAction(R.id.all_bookmarks_menu_id, true);
     }
@@ -1228,6 +1256,7 @@
     private native void nativeOnTabUpdated(long nativeVrShell, boolean incognito, int id,
             String title);
     private native void nativeOnTabRemoved(long nativeVrShell, boolean incognito, int id);
+    private native void nativeOnTabSelected(long nativeVrShell, boolean incognito, int id);
     private native void nativeCloseAlertDialog(long nativeVrShell);
     private native void nativeSetAlertDialog(long nativeVrShell, float width, float height);
     private native void nativeSetDialogBufferSize(long nativeVrShell, float width, float height);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
index 3f5eab9..e8aed82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/OverviewListLayout.java
@@ -80,7 +80,7 @@
                 (FrameLayout.LayoutParams) mTabModelWrapper.getLayoutParams();
         if (params == null) return;
 
-        params.bottomMargin = (int) (getTopBrowserControlsHeight() * mDensity);
+        params.bottomMargin = (int) (getBottomBrowserControlsHeight() * mDensity);
         params.topMargin = (int) (getTopBrowserControlsHeight() * mDensity);
 
         mTabModelWrapper.setLayoutParams(params);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 7f19249d..25f3c88 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -134,9 +134,6 @@
       <message name="IDS_OK_GOT_IT" desc="Label of a button by which the user confirms that they read and understood the information or instructions. Used in multiple contexts. [CHAR-LIMIT=20]">
         OK, got it
       </message>
-      <message name="IDS_GOT_IT" desc="Label of a button by which the user confirms that they read and understood the information or instructions. Used in multiple contexts. [CHAR-LIMIT=20]">
-        Got it
-      </message>
       <message name="IDS_CANCEL" desc="Label for a cancel button. Used in multiple contexts. [CHAR-LIMIT=20]">
         Cancel
       </message>
@@ -798,8 +795,8 @@
       <message name="IDS_MEDIA_PERMISSION_TITLE" desc="Title of the menu containing the media permissions [CHAR-LIMIT=32]">
         Media
       </message>
-      <message name="IDS_POPUP_PERMISSION_TITLE" desc="Title of the permission to display pop-up windows [CHAR-LIMIT=32]">
-        Pop-ups
+      <message name="IDS_POPUP_PERMISSION_TITLE" desc="Title of the permission to display pop-up windows and redirects [CHAR-LIMIT=32]">
+        Pop-ups and redirects
       </message>
       <message name="IDS_PUSH_NOTIFICATIONS_PERMISSION_TITLE" desc="Title for the permission for showing push notifications [CHAR-LIMIT=32]">
         Notifications
@@ -933,8 +930,8 @@
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_USB_BLOCKED" desc="Summary text explaining that the USB permission is set to block all requests for access to devices. To be shown in the list of permission categories.">
         Block sites from connecting to devices
       </message>
-      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_POPUPS_BLOCKED" desc="Summary text explaining that sites are blocked from showing popups and that it is the recommended setting.">
-        Block sites from showing pop-ups (recommended)
+      <message name="IDS_WEBSITE_SETTINGS_CATEGORY_POPUPS_REDIRECTS_BLOCKED" desc="Summary text explaining that sites are blocked from showing popups/redirects and that it is the recommended setting.">
+        Block sites from showing pop-ups and redirects (recommended)
       </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_ADS_BLOCKED" desc="Summary text explaining that ads are being blocked on some sites.">
         Block ads from sites that tend to show intrusive ads
@@ -2240,12 +2237,6 @@
       <message name="IDS_DOWNLOAD_MANAGER_LIST_ITEM_DESCRIPTION" desc="Text containing the download list item description.">
         <ph name="FILE_SIZE">%1$s<ex>1.56 MB</ex></ph> - <ph name="DESCRIPTION">%2$s<ex>www.example.com</ex></ph>
       </message>
-      <message name="IDS_DOWNLOAD_MANAGER_UI_SHOW_STORAGE" desc="Text that tells the user to show the storage information in downloads home.">
-        Show storage
-      </message>
-      <message name="IDS_DOWNLOAD_MANAGER_UI_HIDE_STORAGE" desc="Text that tells the user to hide the storage information in downloads home.">
-        Hide storage
-      </message>
 
       <!-- Browsing History UI -->
       <message name="IDS_HISTORY_MANAGER_EMPTY" desc="Indicates that there are no browsing history items.">
@@ -3287,11 +3278,14 @@
 
       <!-- Interventions -->
       <message name="IDS_REDIRECT_BLOCKED_MESSAGE" desc="The message stating that a redirect (noun) was blocked on this page. This will be followed on a separate line with the address the user was being redirected to.">
-         Redirect blocked to site:
+         Redirect blocked:
       </message>
       <message name="IDS_REDIRECT_BLOCKED_SHORT_MESSAGE" desc="The short message stating that a redirect (noun) was blocked on this page.">
          Redirect blocked.
       </message>
+      <message name="IDS_ALWAYS_ALLOW_REDIRECTS" desc="The infobar button text allowing a user to always allow redirects (noun) after they were blocked on this page.">
+        Always allow
+      </message>
       <message name="IDS_NEAR_OOM_INTERVENTION_MESSAGE" desc="The message stating that the browser intervened to stop the page using too much memory.">
          This page uses too much memory, so Chrome paused it.
       </message>
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_HIDE_STORAGE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_HIDE_STORAGE.png.sha1
deleted file mode 100644
index d265bbc9..0000000
--- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_HIDE_STORAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-86199850f71f7549080fbac10d865d243bfdc950
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_SHOW_STORAGE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_SHOW_STORAGE.png.sha1
deleted file mode 100644
index afe5d01..0000000
--- a/chrome/android/java/strings/android_chrome_strings_grd/IDS_DOWNLOAD_MANAGER_UI_SHOW_STORAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-68c8dff06fca220ad3e2000d6849a8f443368a25
\ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index e8c6bd97..4822894 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1110,6 +1110,7 @@
   "java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
   "java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
   "java/src/org/chromium/chrome/browser/rlz/RlzPingHandler.java",
+  "java/src/org/chromium/chrome/browser/search_engines/TemplateUrl.java",
   "java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchActivityFadingBackgroundView.java",
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index bc9cb0a..901f115 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -24,6 +24,7 @@
   "+content/public/android/java/src/org/chromium/content_public",
   "!content/public/android/java/src/org/chromium/content/browser/BindingManager.java",
   "!content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
+  "!content/public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java",
   "!content/public/android/java/src/org/chromium/content/common/ContentSwitches.java",
 
   "-content/public/android/javatests",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index 56b4387..1232b93 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -32,8 +32,8 @@
 import org.chromium.chrome.browser.locale.DefaultSearchEngineDialogHelperUtils;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.MultiActivityTestRule;
@@ -256,7 +256,7 @@
 
             @Override
             public List<TemplateUrl> getSearchEnginesForPromoDialog(int promoType) {
-                return TemplateUrlService.getInstance().getSearchEngines();
+                return TemplateUrlService.getInstance().getTemplateUrls();
             }
         };
         LocaleManager.setInstanceForTest(mockManager);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
index 85d19fcf7..af23144 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
@@ -21,7 +21,7 @@
 
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.widget.RadioButtonLayout;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
@@ -53,6 +53,34 @@
         }
     }
 
+    private class TestTemplateUrl extends TemplateUrl {
+        private String mShortName;
+        private String mKeyword;
+        public TestTemplateUrl(String shortName, String keyword) {
+            super(0);
+            mShortName = shortName;
+            mKeyword = keyword;
+        }
+
+        @Override
+        public String getShortName() {
+            return mShortName;
+        }
+
+        @Override
+        public String getKeyword() {
+            return mKeyword;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof TemplateUrl)) return false;
+            TestTemplateUrl otherTemplateUrl = (TestTemplateUrl) other;
+            return TextUtils.equals(mKeyword, otherTemplateUrl.mKeyword)
+                    && TextUtils.equals(mShortName, otherTemplateUrl.mShortName);
+        }
+    }
+
     private class TestDialogHelper extends DefaultSearchEngineDialogHelper {
         public TestDelegate delegate;
 
@@ -91,10 +119,10 @@
         mContext = InstrumentationRegistry.getTargetContext();
 
         mTemplateUrls.clear();
-        mTemplateUrls.add(new TemplateUrl(0, "Google: Search by Google", true, "keyword 1"));
-        mTemplateUrls.add(new TemplateUrl(5, "This", true, "keyword 2"));
-        mTemplateUrls.add(new TemplateUrl(10, "That", true, "keyword 3"));
-        mTemplateUrls.add(new TemplateUrl(15, "The Other Thing", true, "keyword 4"));
+        mTemplateUrls.add(new TestTemplateUrl("Google: Search by Google", "keyword 1"));
+        mTemplateUrls.add(new TestTemplateUrl("This", "keyword 2"));
+        mTemplateUrls.add(new TestTemplateUrl("That", "keyword 3"));
+        mTemplateUrls.add(new TestTemplateUrl("The Other Thing", "keyword 4"));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
index 305f59d..55d0da9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
@@ -1,4 +1,3 @@
-// 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.
 
@@ -20,8 +19,8 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
@@ -49,7 +48,7 @@
                 LocaleManager mockManager = new LocaleManager() {
                     @Override
                     public List<TemplateUrl> getSearchEnginesForPromoDialog(int promoType) {
-                        return TemplateUrlService.getInstance().getSearchEngines();
+                        return TemplateUrlService.getInstance().getTemplateUrls();
                     }
                 };
                 LocaleManager.setInstanceForTest(mockManager);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
index f56a265e..827859eb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java
@@ -1,4 +1,3 @@
-// Copyright 2014 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.
 
@@ -31,9 +30,9 @@
 import org.chromium.chrome.browser.preferences.website.ContentSetting;
 import org.chromium.chrome.browser.preferences.website.GeolocationInfo;
 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
@@ -267,7 +266,7 @@
 
     private int indexOfFirstHttpSearchEngine(SearchEnginePreference pref) {
         TemplateUrlService templateUrlService = TemplateUrlService.getInstance();
-        List<TemplateUrl> urls = templateUrlService.getSearchEngines();
+        List<TemplateUrl> urls = templateUrlService.getTemplateUrls();
         int index;
         for (index = 0; index < urls.size(); ++index) {
             String keyword = pref.getKeywordFromIndexForTesting(index);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
index 4a2fe7b..cc1c89b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
@@ -18,8 +18,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
+import org.chromium.chrome.browser.preferences.SearchEngineAdapter;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
 import org.chromium.chrome.browser.test.ClearAppDataTestRule;
 import org.chromium.content.browser.test.util.Criteria;
@@ -145,37 +145,38 @@
                 ThreadUtils.runOnUiThreadBlockingNoException(new Callable<List<TemplateUrl>>() {
                     @Override
                     public List<TemplateUrl> call() throws Exception {
-                        return templateUrlService.getSearchEngines();
+                        return templateUrlService.getTemplateUrls();
                     }
                 });
         // Ensure known state of default search index before running test.
-        String searchEngineKeyword =
-                ThreadUtils.runOnUiThreadBlockingNoException(new Callable<String>() {
+        TemplateUrl defaultSearchEngine =
+                ThreadUtils.runOnUiThreadBlockingNoException(new Callable<TemplateUrl>() {
                     @Override
-                    public String call() throws Exception {
-                        return templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword();
+                    public TemplateUrl call() throws Exception {
+                        return templateUrlService.getDefaultSearchEngineTemplateUrl();
                     }
                 });
-        Assert.assertEquals(searchEngines.get(0).getKeyword(), searchEngineKeyword);
+        SearchEngineAdapter.sortAndFilterUnnecessaryTemplateUrl(searchEngines, defaultSearchEngine);
+        Assert.assertEquals(searchEngines.get(0), defaultSearchEngine);
 
         // Set search engine index and verified it stuck.
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                List<TemplateUrl> searchEngines = templateUrlService.getSearchEngines();
                 Assert.assertTrue(
                         "There must be more than one search engine to change searchEngines",
                         searchEngines.size() > 1);
                 templateUrlService.setSearchEngine(searchEngines.get(1).getKeyword());
             }
         });
-        searchEngineKeyword = ThreadUtils.runOnUiThreadBlockingNoException(new Callable<String>() {
-            @Override
-            public String call() throws Exception {
-                return templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword();
-            }
-        });
-        Assert.assertEquals(searchEngines.get(1).getKeyword(), searchEngineKeyword);
+        defaultSearchEngine =
+                ThreadUtils.runOnUiThreadBlockingNoException(new Callable<TemplateUrl>() {
+                    @Override
+                    public TemplateUrl call() throws Exception {
+                        return templateUrlService.getDefaultSearchEngineTemplateUrl();
+                    }
+                });
+        Assert.assertEquals(searchEngines.get(1), defaultSearchEngine);
     }
 
     @Test
@@ -187,6 +188,14 @@
         // Get the number of prepopulated search engine.
         final int prepopulatedEngineNum = getSearchEngineCount(templateUrlService);
 
+        TemplateUrl defaultSearchEngine =
+                ThreadUtils.runOnUiThreadBlockingNoException(new Callable<TemplateUrl>() {
+                    @Override
+                    public TemplateUrl call() throws Exception {
+                        return templateUrlService.getDefaultSearchEngineTemplateUrl();
+                    }
+                });
+
         // Add custom search engines and verified only engines visited within 2 days are added.
         // Also verified custom engines are sorted correctly.
         List<TemplateUrl> customSearchEngines =
@@ -196,7 +205,9 @@
                         templateUrlService.addSearchEngineForTesting("keyword1", 0);
                         templateUrlService.addSearchEngineForTesting("keyword2", 0);
                         templateUrlService.addSearchEngineForTesting("keyword3", 3);
-                        List<TemplateUrl> searchEngines = templateUrlService.getSearchEngines();
+                        List<TemplateUrl> searchEngines = templateUrlService.getTemplateUrls();
+                        SearchEngineAdapter.sortAndFilterUnnecessaryTemplateUrl(
+                                searchEngines, defaultSearchEngine);
                         return searchEngines.subList(prepopulatedEngineNum, searchEngines.size());
                     }
                 });
@@ -212,7 +223,9 @@
                     public List<TemplateUrl> call() throws Exception {
                         templateUrlService.addSearchEngineForTesting("keyword4", 0);
                         templateUrlService.addSearchEngineForTesting("keyword5", 0);
-                        List<TemplateUrl> searchEngines = templateUrlService.getSearchEngines();
+                        List<TemplateUrl> searchEngines = templateUrlService.getTemplateUrls();
+                        SearchEngineAdapter.sortAndFilterUnnecessaryTemplateUrl(
+                                searchEngines, defaultSearchEngine);
                         return searchEngines.subList(prepopulatedEngineNum, searchEngines.size());
                     }
                 });
@@ -227,7 +240,9 @@
                     @Override
                     public List<TemplateUrl> call() throws Exception {
                         templateUrlService.updateLastVisitedForTesting("keyword3");
-                        List<TemplateUrl> searchEngines = templateUrlService.getSearchEngines();
+                        List<TemplateUrl> searchEngines = templateUrlService.getTemplateUrls();
+                        SearchEngineAdapter.sortAndFilterUnnecessaryTemplateUrl(
+                                searchEngines, defaultSearchEngine);
                         return searchEngines.subList(prepopulatedEngineNum, searchEngines.size());
                     }
                 });
@@ -243,7 +258,11 @@
                     @Override
                     public List<TemplateUrl> call() throws Exception {
                         templateUrlService.setSearchEngine("keyword4");
-                        List<TemplateUrl> searchEngines = templateUrlService.getSearchEngines();
+                        List<TemplateUrl> searchEngines = templateUrlService.getTemplateUrls();
+                        TemplateUrl newDefaultSearchEngine =
+                                templateUrlService.getDefaultSearchEngineTemplateUrl();
+                        SearchEngineAdapter.sortAndFilterUnnecessaryTemplateUrl(
+                                searchEngines, newDefaultSearchEngine);
                         return searchEngines.subList(prepopulatedEngineNum, searchEngines.size());
                     }
                 });
@@ -254,33 +273,11 @@
         Assert.assertEquals("keyword2", customSearchEngines.get(3).getKeyword());
     }
 
-    @Test
-    @SmallTest
-    @Feature({"SearchEngines"})
-    public void testDisableFiltering() {
-        final TemplateUrlService templateUrlService = waitForTemplateUrlServiceToLoad();
-
-        // Get the number of prepopulated search engine.
-        final int prepopulatedEngineNum = getSearchEngineCount(templateUrlService);
-
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            for (int i = 0; i < 10; i++) {
-                templateUrlService.addSearchEngineForTesting("keyword" + i, 0);
-            }
-        });
-
-        Assert.assertEquals(prepopulatedEngineNum + 3, getSearchEngineCount(templateUrlService));
-        templateUrlService.setFilteringEnabled(false);
-        Assert.assertEquals(prepopulatedEngineNum + 10, getSearchEngineCount(templateUrlService));
-        templateUrlService.setFilteringEnabled(true);
-        Assert.assertEquals(prepopulatedEngineNum + 3, getSearchEngineCount(templateUrlService));
-    }
-
     private int getSearchEngineCount(final TemplateUrlService templateUrlService) {
         return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Integer>() {
             @Override
             public Integer call() throws Exception {
-                return templateUrlService.getSearchEngines().size();
+                return templateUrlService.getTemplateUrls().size();
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index 5513303..33d7bc5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -39,8 +39,8 @@
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestion.MatchClassification;
 import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
-import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
 import org.chromium.chrome.browser.searchwidget.SearchActivity.SearchActivityDelegate;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -108,7 +108,7 @@
 
                     @Override
                     public List<TemplateUrl> getSearchEnginesForPromoDialog(int promoType) {
-                        return TemplateUrlService.getInstance().getSearchEngines();
+                        return TemplateUrlService.getInstance().getTemplateUrls();
                     }
                 });
                 super.showSearchEngineDialogIfNeeded(activity, onSearchEngineFinalized);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/BrandColorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/BrandColorTest.java
index 494e0bf..6435c86 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/BrandColorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/BrandColorTest.java
@@ -34,7 +34,7 @@
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.DisableInTabbedMode;
-import org.chromium.content.browser.test.InterstitialPageDelegateAndroid;
+import org.chromium.content.browser.InterstitialPageDelegateAndroid;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 import org.chromium.ui.test.util.UiRestriction;
@@ -249,7 +249,10 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                delegate.showInterstitialPage(brandColorUrl, mActivityTestRule.getWebContents());
+                mActivityTestRule.getActivity()
+                        .getActivityTab()
+                        .getWebContents()
+                        .showInterstitialPage(brandColorUrl, delegate.getNative());
             }
         });
         CriteriaHelper.pollUiThread(new Criteria() {
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 687b805..84647d0 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -215,6 +215,9 @@
   <message name="IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL" desc="Downloads local directory label.">
     Downloads
   </message>
+  <message name="IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL" desc="A label for the 'Linux Files' root which shows crostini files.">
+    Linux Files
+  </message>
   <message name="IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL" desc="A label for the 'Images' root of media views.">
     Images
   </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3b63bb109..0eeacab 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10024,9 +10024,6 @@
       <message name="IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY" desc="Body text of an auto-generated notification informing the user that the site may have received an update in the background, in case the site did not satisfy the visible notification requirement itself.">
         This site has been updated in the background.
       </message>
-      <message name="IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME" desc="The name of the Notifications service that can run in the background">
-        Notifications
-      </message>
 
       <!-- Easy Unlock strings -->
       <!-- Strings for the Easy Unlock promo notification -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 31d73e0f..f8e06f4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -820,8 +820,6 @@
     "net/spdyproxy/data_reduction_proxy_chrome_settings.h",
     "net/spdyproxy/data_reduction_proxy_chrome_settings_factory.cc",
     "net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h",
-    "net/sth_distributor_provider.cc",
-    "net/sth_distributor_provider.h",
     "net/system_network_context_manager.cc",
     "net/system_network_context_manager.h",
     "net/timed_cache.cc",
@@ -2339,6 +2337,8 @@
       "profiles/profile_android.h",
       "search/contextual_search_policy_handler_android.cc",
       "search/contextual_search_policy_handler_android.h",
+      "search_engines/template_url_android.cc",
+      "search_engines/template_url_android.h",
       "search_engines/template_url_service_android.cc",
       "search_engines/template_url_service_android.h",
       "signin/oauth2_token_service_delegate_android.cc",
@@ -4405,6 +4405,7 @@
       "../android/java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
       "../android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
       "../android/java/src/org/chromium/chrome/browser/rlz/RlzPingHandler.java",
+      "../android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrl.java",
       "../android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java",
       "../android/java/src/org/chromium/chrome/browser/sessions/SessionTabHelper.java",
       "../android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 830ca6d8..99e57ce5 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1561,9 +1561,6 @@
     {"multidevice-service", flag_descriptions::kMultiDeviceApiName,
      flag_descriptions::kMultiDeviceApiDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kMultiDeviceApi)},
-    {"mus", flag_descriptions::kUseMusName,
-     flag_descriptions::kUseMusDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kMus)},
     {"mash", flag_descriptions::kUseMashName,
      flag_descriptions::kUseMashDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kMash)},
@@ -1894,10 +1891,6 @@
      flag_descriptions::kChromeHomeInactivitySheetExpansionDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kChromeHomeInactivitySheetExpansion)},
-    {"enable-chrome-home-menu-items",
-     flag_descriptions::kChromeHomeMenuItemsName,
-     flag_descriptions::kChromeHomeMenuItemsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kChromeHomeMenuItemsExpandSheet)},
     {"chrome-home-swipe-logic", flag_descriptions::kChromeHomeSwipeLogicName,
      flag_descriptions::kChromeHomeSwipeLogicDescription, kOsAndroid,
      MULTI_VALUE_TYPE(kChromeHomeSwipeLogicChoices)},
@@ -2389,14 +2382,6 @@
      flag_descriptions::kUseWinrtMidiApiDescription, kOsWin,
      FEATURE_VALUE_TYPE(midi::features::kMidiManagerWinrt)},
 #endif  // OS_WIN
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-    {"enable-push-api-background-mode",
-     flag_descriptions::kPushApiBackgroundModeName,
-     flag_descriptions::kPushApiBackgroundModeDescription,
-     kOsMac | kOsWin | kOsLinux,
-     ENABLE_DISABLE_VALUE_TYPE(switches::kEnablePushApiBackgroundMode,
-                               switches::kDisablePushApiBackgroundMode)},
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
 #if defined(OS_CHROMEOS)
     {"cros-regions-mode", flag_descriptions::kCrosRegionsModeName,
      flag_descriptions::kCrosRegionsModeDescription, kOsCrOS,
@@ -3821,12 +3806,12 @@
      FEATURE_VALUE_TYPE(features::kViewsCastDialog)},
 #endif  // defined(TOOLKIT_VIEWS)
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
     {"enable-emoji-context-menu",
      flag_descriptions::kEnableEmojiContextMenuName,
-     flag_descriptions::kEnableEmojiContextMenuDescription, kOsMac,
+     flag_descriptions::kEnableEmojiContextMenuDescription, kOsMac | kOsWin,
      FEATURE_VALUE_TYPE(features::kEnableEmojiContextMenu)},
-#endif  // OS_MACOSX
+#endif  // OS_MACOSX || OS_WIN
 
     {"SupervisedUserCommittedInterstitials",
      flag_descriptions::kSupervisedUserCommittedInterstitialsName,
@@ -3922,6 +3907,14 @@
      FEATURE_VALUE_TYPE(features::kDisplayCutoutAPI)},
 #endif  // OS_ANDROID
 
+#if defined(USE_AURA)
+    {"touchpad-overscroll-history-navigation",
+     flag_descriptions::kTouchpadOverscrollHistoryNavigationName,
+     flag_descriptions::kTouchpadOverscrollHistoryNavigationDescription,
+     kOsWin | kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kTouchpadOverscrollHistoryNavigation)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
index 42cc6cf..a81113c 100644
--- a/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
+++ b/chrome/browser/android/bookmarks/partner_bookmarks_reader.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "components/favicon_base/favicon_types.h"
@@ -313,9 +314,10 @@
         })");
   GetLargeIconService()
       ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          page_url, kPartnerBookmarksMinimumFaviconSizePx,
-          desired_favicon_size_px, false /* may_page_url_be_private */,
-          traffic_annotation,
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              page_url, kPartnerBookmarksMinimumFaviconSizePx,
+              desired_favicon_size_px),
+          false /* may_page_url_be_private */, traffic_annotation,
           base::Bind(&PartnerBookmarksReader::OnGetFaviconFromServerFinished,
                      base::Unretained(this), page_url, desired_favicon_size_px,
                      base::Passed(std::move(callback))));
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index f879dd0..9ae9c6d 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -71,7 +71,6 @@
     &kCCTRedirectPreconnect,
     &kChromeDuplexFeature,
     &kChromeHomeInactivitySheetExpansion,
-    &kChromeHomeMenuItemsExpandSheet,
     &kChromeHomePersistentIph,
     &kChromeHomePullToRefreshIphAtTop,
     &kChromeHomeSwipeLogic,
@@ -205,9 +204,6 @@
 const base::Feature kChromeHomeInactivitySheetExpansion{
     "ChromeHomeInactivitySheetExpansion", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kChromeHomeMenuItemsExpandSheet{
-    "ChromeHomeMenuItemsExpandSheet", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kChromeHomePersistentIph{"ChromeHomePersistentIph",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 3331923..9007eb3 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -24,7 +24,6 @@
 extern const base::Feature kCCTRedirectPreconnect;
 extern const base::Feature kChromeDuplexFeature;
 extern const base::Feature kChromeHomeInactivitySheetExpansion;
-extern const base::Feature kChromeHomeMenuItemsExpandSheet;
 extern const base::Feature kChromeHomePersistentIph;
 extern const base::Feature kChromeHomePullToRefreshIphAtTop;
 extern const base::Feature kChromeHomeSurvey;
diff --git a/chrome/browser/android/compositor/compositor_view.cc b/chrome/browser/android/compositor/compositor_view.cc
index 387164d..d0c90c9 100644
--- a/chrome/browser/android/compositor/compositor_view.cc
+++ b/chrome/browser/android/compositor/compositor_view.cc
@@ -127,9 +127,11 @@
   Java_CompositorView_didSwapFrame(env, obj_, pending_frames);
 }
 
-void CompositorView::DidSwapBuffers() {
+void CompositorView::DidSwapBuffers(const gfx::Size& swap_size) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_CompositorView_didSwapBuffers(env, obj_);
+  bool swapped_current_size =
+      swap_size == gfx::Size(content_width_, content_height_);
+  Java_CompositorView_didSwapBuffers(env, obj_, swapped_current_size);
 }
 
 ui::UIResourceProvider* CompositorView::GetUIResourceProvider() {
diff --git a/chrome/browser/android/compositor/compositor_view.h b/chrome/browser/android/compositor/compositor_view.h
index 1074f5b0..d994948 100644
--- a/chrome/browser/android/compositor/compositor_view.h
+++ b/chrome/browser/android/compositor/compositor_view.h
@@ -89,7 +89,7 @@
   // CompositorClient implementation:
   void UpdateLayerTreeHost() override;
   void DidSwapFrame(int pending_frames) override;
-  void DidSwapBuffers() override;
+  void DidSwapBuffers(const gfx::Size& swap_size) override;
   ui::UIResourceProvider* GetUIResourceProvider();
 
  private:
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
index f33ebc7..0b2697b 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
@@ -132,6 +132,10 @@
   }
 }
 
+void OomInterventionTabHelper::DeclineInterventionSticky() {
+  NOTREACHED();
+}
+
 void OomInterventionTabHelper::WebContentsDestroyed() {
   OutOfMemoryReporter::FromWebContents(web_contents())->RemoveObserver(this);
   StopMonitoring();
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
index 98916d1..c43472b 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
@@ -43,6 +43,7 @@
   // InterventionDelegate:
   void AcceptIntervention() override;
   void DeclineIntervention() override;
+  void DeclineInterventionSticky() override;
 
  private:
   explicit OomInterventionTabHelper(content::WebContents* web_contents);
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc
index 703dac98..207e981 100644
--- a/chrome/browser/android/vr/vr_gl_thread.cc
+++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -249,6 +249,13 @@
       base::BindOnce(&VrShell::OpenNewTab, weak_vr_shell_, incognito));
 }
 
+void VrGLThread::SelectTab(int id, bool incognito) {
+  DCHECK(OnGlThread());
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VrShell::SelectTab, weak_vr_shell_, id, incognito));
+}
+
 void VrGLThread::OpenBookmarks() {
   DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
@@ -539,6 +546,13 @@
       base::BindOnce(&BrowserUiInterface::RemoveAllTabs, weak_browser_ui_));
 }
 
+void VrGLThread::OnTabSelected(int id, bool incognito) {
+  DCHECK(OnMainThread());
+  task_runner()->PostTask(FROM_HERE,
+                          base::BindOnce(&BrowserUiInterface::OnTabSelected,
+                                         weak_browser_ui_, id, incognito));
+}
+
 void VrGLThread::ReportUiActivityResultForTesting(
     const VrUiTestActivityResult& result) {
   DCHECK(OnGlThread());
diff --git a/chrome/browser/android/vr/vr_gl_thread.h b/chrome/browser/android/vr/vr_gl_thread.h
index 9883f1e0b..60b600d 100644
--- a/chrome/browser/android/vr/vr_gl_thread.h
+++ b/chrome/browser/android/vr/vr_gl_thread.h
@@ -87,6 +87,7 @@
   void NavigateForward() override;
   void ReloadTab() override;
   void OpenNewTab(bool incognito) override;
+  void SelectTab(int id, bool incognito) override;
   void OpenBookmarks() override;
   void OpenRecentTabs() override;
   void OpenHistory() override;
@@ -138,6 +139,7 @@
                       const base::string16& title) override;
   void RemoveTab(int id, bool incognito) override;
   void RemoveAllTabs() override;
+  void OnTabSelected(int id, bool incognito) override;
 
   void ReportUiActivityResultForTesting(
       const VrUiTestActivityResult& result) override;
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc
index 871b4bb..0d6629d 100644
--- a/chrome/browser/android/vr/vr_shell.cc
+++ b/chrome/browser/android/vr/vr_shell.cc
@@ -372,6 +372,11 @@
   Java_VrShellImpl_openNewTab(env, j_vr_shell_, incognito);
 }
 
+void VrShell::SelectTab(int id, bool incognito) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_VrShellImpl_selectTab(env, j_vr_shell_, id, incognito);
+}
+
 void VrShell::OpenBookmarks() {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_VrShellImpl_openBookmarks(env, j_vr_shell_);
@@ -624,6 +629,13 @@
   ui_->RemoveTab(id, incognito);
 }
 
+void VrShell::OnTabSelected(JNIEnv* env,
+                            const JavaParamRef<jobject>& obj,
+                            jboolean incognito,
+                            jint id) {
+  ui_->OnTabSelected(id, incognito);
+}
+
 void VrShell::SetAlertDialog(JNIEnv* env,
                              const base::android::JavaParamRef<jobject>& obj,
                              float width,
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h
index 3400c2c..5f5b475 100644
--- a/chrome/browser/android/vr/vr_shell.h
+++ b/chrome/browser/android/vr/vr_shell.h
@@ -129,12 +129,17 @@
                     const base::android::JavaParamRef<jobject>& obj,
                     jboolean incognito,
                     jint id);
+  void OnTabSelected(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& obj,
+                     jboolean incognito,
+                     jint id);
   void OnContentPaused(bool paused);
   void Navigate(GURL url, NavigationMethod method);
   void NavigateBack();
   void NavigateForward();
   void ReloadTab();
   void OpenNewTab(bool incognito);
+  void SelectTab(int id, bool incognito);
   void OpenBookmarks();
   void OpenRecentTabs();
   void OpenHistory();
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc
index 2c91c17..8a29faf 100644
--- a/chrome/browser/autocomplete/autocomplete_browsertest.cc
+++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc
@@ -57,7 +57,7 @@
 
 }  // namespace
 
-class AutocompleteBrowserTest : public ExtensionBrowserTest {
+class AutocompleteBrowserTest : public extensions::ExtensionBrowserTest {
  protected:
   void WaitForTemplateURLServiceToLoad() {
     search_test_utils::WaitForTemplateURLServiceToLoad(
diff --git a/chrome/browser/chrome_security_exploit_browsertest.cc b/chrome/browser/chrome_security_exploit_browsertest.cc
index 02a3a9ee..f2a13b3 100644
--- a/chrome/browser/chrome_security_exploit_browsertest.cc
+++ b/chrome/browser/chrome_security_exploit_browsertest.cc
@@ -35,13 +35,14 @@
 // perform any dangerous operations in such cases.
 // This is similar to the security_exploit_browsertest.cc tests, but also
 // includes chrome/ layer concepts such as extensions.
-class ChromeSecurityExploitBrowserTest : public ExtensionBrowserTest {
+class ChromeSecurityExploitBrowserTest
+    : public extensions::ExtensionBrowserTest {
  public:
   ChromeSecurityExploitBrowserTest() {}
   ~ChromeSecurityExploitBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
     ASSERT_TRUE(embedded_test_server()->Start());
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -50,7 +51,7 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     // Since we assume exploited renderer process, it can bypass the same origin
     // policy at will. Simulate that by passing the disable-web-security flag.
     command_line->AppendSwitch(switches::kDisableWebSecurity);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index db86a24..9ace0bc 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -33,6 +33,7 @@
     "//chromeos:vm_applications_apps_proto",
     "//components/policy/proto",
     "//content/app/resources",
+    "//ui/accessibility/mojom",
     "//ui/chromeos/resources",
     "//ui/chromeos/strings",
     "//ui/resources",
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
index 94e10fe9..7f522ef9 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
@@ -31,7 +31,8 @@
 #include "components/arc/connection_holder.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
-#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
+#include "ui/accessibility/ax_assistant_structure.h"
+#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/gfx/geometry/dip_util.h"
@@ -49,30 +50,33 @@
     base::TimeDelta::FromMinutes(1);
 
 mojom::VoiceInteractionStructurePtr CreateVoiceInteractionStructure(
-    const ui::AXSnapshotNodeAndroid& view_structure) {
+    const ui::AssistantTree& tree,
+    const ui::AssistantNode& node) {
   auto structure = mojom::VoiceInteractionStructure::New();
-  structure->text = view_structure.text;
-  structure->text_size = view_structure.text_size;
+  structure->text = node.text;
+  structure->text_size = node.text_size;
 
-  structure->bold = view_structure.bold;
-  structure->italic = view_structure.italic;
-  structure->underline = view_structure.underline;
-  structure->line_through = view_structure.line_through;
-  structure->color = view_structure.color;
-  structure->bgcolor = view_structure.bgcolor;
+  structure->bold = node.bold;
+  structure->italic = node.italic;
+  structure->underline = node.underline;
+  structure->line_through = node.line_through;
+  structure->color = node.color;
+  structure->bgcolor = node.bgcolor;
 
-  structure->role = view_structure.role;
+  structure->role = node.role;
 
-  structure->class_name = view_structure.class_name;
-  structure->rect = view_structure.rect;
+  structure->class_name = node.class_name;
+  structure->rect = node.rect;
 
-  if (view_structure.has_selection) {
-    structure->selection = gfx::Range(view_structure.start_selection,
-                                      view_structure.end_selection);
+  if (node.selection.has_value()) {
+    structure->selection =
+        gfx::Range(node.selection->start(), node.selection->end());
   }
 
-  for (auto& child : view_structure.children)
-    structure->children.push_back(CreateVoiceInteractionStructure(*child));
+  for (int child : node.children_indices) {
+    structure->children.push_back(
+        CreateVoiceInteractionStructure(tree, *tree.nodes[child]));
+  }
 
   return structure;
 }
@@ -97,8 +101,10 @@
   title_node->rect = gfx::Rect(bounds.size());
   title_node->class_name = "android.view.dummy.WebTitle";
   title_node->text = title;
+
+  auto assistant_tree = ui::CreateAssistantTree(update, false);
   title_node->children.push_back(CreateVoiceInteractionStructure(
-      *ui::AXSnapshotNodeAndroid::Create(update, false)));
+      *assistant_tree, *assistant_tree->nodes.front()));
   root->children.push_back(std::move(title_node));
   std::move(callback).Run(std::move(root));
 }
@@ -334,8 +340,9 @@
 // static
 mojom::VoiceInteractionStructurePtr
 ArcVoiceInteractionArcHomeService::CreateVoiceInteractionStructureForTesting(
-    const ui::AXSnapshotNodeAndroid& view_structure) {
-  return CreateVoiceInteractionStructure(view_structure);
+    const ui::AssistantTree& tree,
+    const ui::AssistantNode& node) {
+  return CreateVoiceInteractionStructure(tree, node);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h
index 1dc704f..7ed239d3 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h
@@ -20,8 +20,9 @@
 }  // namespace content
 
 namespace ui {
-struct AXSnapshotNodeAndroid;
-}  // ui
+struct AssistantTree;
+struct AssistantNode;
+}  // namespace ui
 
 namespace arc {
 
@@ -66,8 +67,8 @@
   void OnVoiceInteractionOobeSetupComplete() override;
 
   static mojom::VoiceInteractionStructurePtr
-  CreateVoiceInteractionStructureForTesting(
-      const ui::AXSnapshotNodeAndroid& view_structure);
+  CreateVoiceInteractionStructureForTesting(const ui::AssistantTree& tree,
+                                            const ui::AssistantNode& node);
 
   void set_assistant_started_timeout_for_testing(
       const base::TimeDelta& timeout) {
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service_browsertest.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service_browsertest.cc
index 9a68ae0..9cd93bd 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service_browsertest.cc
@@ -17,8 +17,8 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
+#include "ui/accessibility/ax_assistant_structure.h"
 #include "ui/accessibility/ax_tree_update.h"
-#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
 
 namespace arc {
 
@@ -63,9 +63,11 @@
                        base::Unretained(&waiter)),
         ui::kAXModeComplete);
     waiter.Wait();
-    auto node = ui::AXSnapshotNodeAndroid::Create(waiter.snapshot(), false);
+    std::unique_ptr<ui::AssistantTree> tree =
+        ui::CreateAssistantTree(waiter.snapshot(), false);
+
     return ArcVoiceInteractionArcHomeService::
-        CreateVoiceInteractionStructureForTesting(*node);
+        CreateVoiceInteractionStructureForTesting(*tree, *tree->nodes.front());
   }
 
  private:
diff --git a/chrome/browser/chromeos/ash_config.cc b/chrome/browser/chromeos/ash_config.cc
index 54d61e5..0c18f36f 100644
--- a/chrome/browser/chromeos/ash_config.cc
+++ b/chrome/browser/chromeos/ash_config.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/chromeos/ash_config.h"
 
-#include "services/service_manager/runner/common/client_util.h"
 #include "ui/base/ui_base_features.h"
 
 namespace chromeos {
@@ -12,15 +11,8 @@
 namespace {
 
 ash::Config ComputeAshConfig() {
-  ash::Config config = ash::Config::CLASSIC;
-  if (base::FeatureList::IsEnabled(features::kMash))
-    config = ash::Config::MASH;
-  else if (base::FeatureList::IsEnabled(features::kMus))
-    config = ash::Config::MUS;
-  VLOG_IF(1, config != ash::Config::CLASSIC &&
-                 !service_manager::ServiceManagerIsRemote())
-      << " Running with a simulated ash config (likely for testing).";
-  return config;
+  return base::FeatureList::IsEnabled(features::kMash) ? ash::Config::MASH
+                                                       : ash::Config::CLASSIC;
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index 18d9170..bf93713 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -11,11 +11,13 @@
 #include <utility>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
@@ -41,6 +43,7 @@
 #include "chrome/common/extensions/api/manifest_types.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/services/file_util/public/cpp/zip_file_creator.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/settings/timezone_settings.h"
 #include "components/account_id/account_id.h"
 #include "components/drive/drive_pref_names.h"
@@ -640,6 +643,25 @@
   Respond(NoArguments());
 }
 
+bool IsCrostiniEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+             chromeos::switches::kCrostiniFiles) &&
+         crostini::CrostiniManager::IsCrosTerminaInstalled();
+}
+
+ExtensionFunction::ResponseAction
+FileManagerPrivateIsCrostiniEnabledFunction::Run() {
+  return RespondNow(
+      OneArgument(std::make_unique<base::Value>(IsCrostiniEnabled())));
+}
+
+ExtensionFunction::ResponseAction
+FileManagerPrivateMountCrostiniContainerFunction::Run() {
+  // TOOD(https://crbug.com/832509): implement MountCrostiniContainer.
+  DCHECK(IsCrostiniEnabled());
+  return RespondNow(NoArguments());
+}
+
 FileManagerPrivateInternalGetCustomActionsFunction::
     FileManagerPrivateInternalGetCustomActionsFunction()
     : chrome_details_(this) {}
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
index 3be6e5d..c745985 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -261,6 +261,34 @@
   DISALLOW_COPY_AND_ASSIGN(FileManagerPrivateConfigureVolumeFunction);
 };
 
+// Implements the chrome.fileManagerPrivate.isCrostiniEnabled method.
+// Gets crostini sftp mount params.
+class FileManagerPrivateIsCrostiniEnabledFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileManagerPrivate.isCrostiniEnabled",
+                             FILEMANAGERPRIVATE_ISCROSTINIENABLED)
+
+ protected:
+  ~FileManagerPrivateIsCrostiniEnabledFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+// Implements the chrome.fileManagerPrivate.mountCrostiniContainer method.
+// Starts and mounts crostini container.
+class FileManagerPrivateMountCrostiniContainerFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileManagerPrivate.mountCrostiniContainer",
+                             FILEMANAGERPRIVATE_MOUNTCROSTINICONTAINER)
+
+ protected:
+  ~FileManagerPrivateMountCrostiniContainerFunction() override {}
+
+  ResponseAction Run() override;
+};
+
 // Implements the chrome.fileManagerPrivate.getCustomActions method.
 class FileManagerPrivateInternalGetCustomActionsFunction
     : public UIThreadExtensionFunction {
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 018ea447..5eafd139 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -724,6 +724,7 @@
   SET_STRING("TOTAL_FILE_COUNT", IDS_FILE_BROWSER_TOTAL_FILE_COUNT_LABEL);
   SET_STRING("IMAGE_RESOLUTION_COLUMN_LABEL",
              IDS_FILE_BROWSER_IMAGE_RESOLUTION_COLUMN_LABEL);
+  SET_STRING("LINUX_FILES_ROOT_LABEL", IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL);
   SET_STRING("MEDIA_ARTIST_COLUMN_LABEL",
              IDS_FILE_BROWSER_MEDIA_ARTIST_COLUMN_LABEL);
   SET_STRING("MEDIA_TITLE_COLUMN_LABEL",
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
index c737767..b4692393 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
@@ -217,7 +217,7 @@
   const user_manager::User* const user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(
           chrome_details_.GetProfile());
-  chromeos::UserContext user_context(user->GetAccountId());
+  chromeos::UserContext user_context(*user);
   user_context.SetKey(chromeos::Key(params->account_password));
 
   // Alter |user_context| if the user is supervised.
@@ -554,7 +554,7 @@
   const user_manager::User* const user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(
           chrome_details_.GetProfile());
-  const chromeos::UserContext user_context(user->GetAccountId());
+  const chromeos::UserContext user_context(*user);
   chromeos::EasyUnlockService::Get(chrome_details_.GetProfile())
       ->HandleUserReauth(user_context);
 
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
index 472e795c..cba573f 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -80,7 +80,7 @@
     AuthStatusConsumer* auth_status_consumer) {
   const AccountId account_id =
       AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId);
-  UserContext expected_context(account_id);
+  UserContext expected_context(user_manager::USER_TYPE_REGULAR, account_id);
   expected_context.SetKey(Key(kValidPassword));
 
   ExtendedAuthenticator* authenticator =
diff --git a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
index ccdc360..2321ff2 100644
--- a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
@@ -5,15 +5,22 @@
 #include "chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h"
 
 #include "chromeos/chromeos_switches.h"
+#include "media/base/media_switches.h"
 
 namespace file_manager {
 
+static constexpr bool kUseFakeAudioLayer = true;
+
 template <GuestMode MODE>
 class VideoPlayerBrowserTestBase : public FileManagerBrowserTestBase {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    if (kUseFakeAudioLayer)
+      command_line->AppendSwitch(switches::kDisableAudioOutput);
+
     command_line->AppendSwitch(
         chromeos::switches::kEnableVideoPlayerChromecastSupport);
+
     FileManagerBrowserTestBase::SetUpCommandLine(command_line);
   }
 
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
index 025e08c1..2a00be6 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
@@ -55,15 +55,14 @@
 };
 
 class InputMethodEngineBrowserTest
-    : public ExtensionBrowserTest,
+    : public extensions::ExtensionBrowserTest,
       public ::testing::WithParamInterface<TestType> {
  public:
-  InputMethodEngineBrowserTest()
-      : ExtensionBrowserTest() {}
+  InputMethodEngineBrowserTest() : extensions::ExtensionBrowserTest() {}
   virtual ~InputMethodEngineBrowserTest() {}
 
   void SetUpInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
   }
 
   void TearDownInProcessBrowserTestFixture() override { extension_ = NULL; }
diff --git a/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc b/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
index 7a37cd9..5062e812 100644
--- a/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/note_taking_browsertest.cc
@@ -70,7 +70,7 @@
   DISALLOW_COPY_AND_ASSIGN(LockScreenAppsEnabledWaiter);
 };
 
-class LockScreenNoteTakingTest : public ExtensionBrowserTest {
+class LockScreenNoteTakingTest : public extensions::ExtensionBrowserTest {
  public:
   LockScreenNoteTakingTest() { set_chromeos_user_ = true; }
   ~LockScreenNoteTakingTest() override = default;
@@ -80,7 +80,7 @@
                                 kTestAppId);
     cmd_line->AppendSwitch(ash::switches::kAshForceEnableStylusTools);
 
-    ExtensionBrowserTest::SetUpCommandLine(cmd_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(cmd_line);
   }
 
   bool EnableLockScreenAppLaunch(const std::string& app_id) {
diff --git a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
index 783c605..12fa986 100644
--- a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
+++ b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
@@ -192,7 +192,8 @@
 class CryptohomeAuthenticatorTest : public testing::Test {
  public:
   CryptohomeAuthenticatorTest()
-      : user_context_(AccountId::FromUserEmail("me@nowhere.org")),
+      : user_context_(user_manager::USER_TYPE_REGULAR,
+                      AccountId::FromUserEmail("me@nowhere.org")),
         user_manager_(new chromeos::FakeChromeUserManager()),
         user_manager_enabler_(base::WrapUnique(user_manager_)),
         mock_caller_(NULL),
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher_browsertest.cc b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher_browsertest.cc
index 0db0930..1ad00ce 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_app_launcher_browsertest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_app_launcher_browsertest.cc
@@ -60,7 +60,7 @@
 
 }  // namespace
 
-class DemoAppLauncherTest : public ExtensionBrowserTest {
+class DemoAppLauncherTest : public extensions::ExtensionBrowserTest {
  public:
   DemoAppLauncherTest() { set_exit_when_last_browser_closes(false); }
 
@@ -78,7 +78,7 @@
 
   void SetUp() override {
     chromeos::DemoAppLauncher::SetDemoAppPathForTesting(GetTestDemoAppPath());
-    ExtensionBrowserTest::SetUp();
+    extensions::ExtensionBrowserTest::SetUp();
   }
 
  private:
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
index 7b62f7dc..743088e0 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -476,8 +476,11 @@
       UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
   DCHECK(key_manager);
 
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
   key_manager->GetDeviceDataList(
-      UserContext(account_id),
+      UserContext(*user),
       base::Bind(&EasyUnlockService::OnCryptohomeKeysFetchedForChecking,
                  weak_ptr_factory_.GetWeakPtr(), account_id, paired_devices));
 }
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index a228cf60..4b9e906 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -95,8 +95,11 @@
       UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
   DCHECK(key_manager);
 
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
   key_manager->GetDeviceDataList(
-      UserContext(account_id),
+      UserContext(*user),
       base::Bind(&RetryDataLoadOnError, account_id, backoff_ms, callback));
 }
 
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 522f81fc..03c626a 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -983,8 +983,9 @@
   // Inform |auth_status_consumer_| about successful login.
   // TODO(nkostylev): Pass UserContext back crbug.com/424550
   if (auth_status_consumer_) {
-    auth_status_consumer_->OnAuthSuccess(
-        UserContext(last_login_attempt_account_id_));
+    const user_manager::User* const user =
+        chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+    auth_status_consumer_->OnAuthSuccess(UserContext(*user));
   }
 }
 
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index a185c4c..c428624 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -243,7 +243,9 @@
 
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerTest, DISABLED_ExistingUserLogin) {
   EXPECT_CALL(*mock_login_display_, SetUIEnabled(false)).Times(2);
-  UserContext user_context(gaia_account_id_);
+  const user_manager::User* user =
+      user_manager::UserManager::Get()->FindUser(gaia_account_id_);
+  UserContext user_context(*user);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   test::UserSessionManagerTestApi session_manager_test_api(
@@ -290,7 +292,8 @@
 
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerUntrustedTest,
                        ExistingUserLoginForbidden) {
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   existing_user_controller()->Login(user_context, SigninSpecifics());
@@ -304,7 +307,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerUntrustedTest,
                        MAYBE_NewUserLoginForbidden) {
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   existing_user_controller()->CompleteLogin(user_context);
@@ -319,7 +323,8 @@
 
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerUntrustedTest,
                        SupervisedUserLoginForbidden) {
-  UserContext user_context(AccountId::FromUserEmail(kSupervisedUserID));
+  UserContext user_context(user_manager::UserType::USER_TYPE_SUPERVISED,
+                           AccountId::FromUserEmail(kSupervisedUserID));
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   existing_user_controller()->Login(user_context, SigninSpecifics());
@@ -588,7 +593,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerPublicSessionTest,
                        DISABLED_LoginStopsAutoLogin) {
   // Set up mocks to check login success.
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(user_context.GetAccountId().GetUserEmail());
   ExpectSuccessfulLogin(user_context);
@@ -620,7 +626,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerPublicSessionTest,
                        GuestModeLoginStopsAutoLogin) {
   EXPECT_CALL(*mock_login_display_, SetUIEnabled(false)).Times(2);
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   test::UserSessionManagerTestApi session_manager_test_api(
       UserSessionManager::GetInstance());
@@ -649,7 +656,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerPublicSessionTest,
                        CompleteLoginStopsAutoLogin) {
   // Set up mocks to check login success.
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(user_context.GetAccountId().GetUserEmail());
   ExpectSuccessfulLogin(user_context);
@@ -719,7 +727,8 @@
 
   // Check that the attempt to start a public session fails with an error.
   ExpectLoginFailure();
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(user_context.GetAccountId().GetUserEmail());
   existing_user_controller()->Login(user_context, SigninSpecifics());
@@ -910,7 +919,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
                        ActiveDirectoryOnlineLogin_Success) {
   ExpectLoginSuccess();
-  UserContext user_context(ad_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
+                           ad_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(ad_account_id_.GetUserEmail());
   user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
@@ -930,7 +940,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
                        PolicyChangeTriggersFileUpdate) {
   ExpectLoginSuccess();
-  UserContext user_context(ad_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
+                           ad_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(ad_account_id_.GetUserEmail());
   user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
@@ -956,7 +967,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
                        ActiveDirectoryOfflineLogin_Success) {
   ExpectLoginSuccess();
-  UserContext user_context(ad_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
+                           ad_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(ad_account_id_.GetUserEmail());
   ASSERT_EQ(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
@@ -975,7 +987,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
                        GAIAAccountLogin_Failure) {
   ExpectLoginFailure();
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   existing_user_controller()->CompleteLogin(user_context);
@@ -985,7 +998,8 @@
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryUserWhitelistTest,
                        Success) {
   ExpectLoginSuccess();
-  UserContext user_context(ad_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
+                           ad_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(ad_account_id_.GetUserEmail());
   user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
@@ -1005,7 +1019,8 @@
   ExpectLoginWhitelistFailure();
   AccountId account_id =
       AccountId::AdFromUserEmailObjGuid(kUserNotMatchingWhitelist, kGaiaID);
-  UserContext user_context(account_id);
+  UserContext user_context(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
+                           account_id);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(account_id.GetUserEmail());
   user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
@@ -1029,7 +1044,8 @@
 // profile prefs.
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerSavePasswordHashTest,
                        GaiaOnlineLoginSavesPasswordHashToPrefs) {
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   content::WindowedNotificationObserver profile_prepared_observer(
@@ -1052,7 +1068,8 @@
 // prefs.
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerSavePasswordHashTest,
                        OfflineLoginSavesPasswordHashToPrefs) {
-  UserContext user_context(gaia_account_id_);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           gaia_account_id_);
   user_context.SetKey(Key(kPassword));
   user_context.SetUserIDHash(gaia_account_id_.GetUserEmail());
   content::WindowedNotificationObserver profile_prepared_observer(
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc
index 61630bb..5091a56 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -693,7 +693,7 @@
     return;
   }
 
-  UserContext user_context(active_user->GetType(), active_user->GetAccountId());
+  UserContext user_context(*active_user);
   if (!base::ContainsKey(matches, active_user->username_hash())) {
     OnFingerprintAuthFailure(*active_user);
     return;
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
index d3475f8c..446da6ec 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
@@ -169,7 +169,8 @@
   EXPECT_GT(lock_bounds.width(), 10);
   EXPECT_GT(lock_bounds.height(), 10);
 
-  UserContext user_context(user_manager::StubAccountId());
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           user_manager::StubAccountId());
   user_context.SetKey(Key("pass"));
   tester->InjectStubUserContext(user_context);
   EXPECT_TRUE(tester->IsLocked());
@@ -227,7 +228,8 @@
     EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
     EXPECT_TRUE(tester->IsLocked());
   }
-  UserContext user_context(user_manager::StubAccountId());
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           user_manager::StubAccountId());
   user_context.SetKey(Key("pass"));
   tester->InjectStubUserContext(user_context);
   tester->EnterPassword("pass");
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 923ea9be..7c8e3a85 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -172,7 +172,10 @@
          quick_unlock_storage->IsPinAuthenticationAvailable() ||
          !authenticated_by_pin);
 
-  UserContext user_context(account_id);
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
+  UserContext user_context(*user);
   Key::KeyType key_type =
       authenticated_by_pin ? chromeos::Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234
                            : chromeos::Key::KEY_TYPE_SALTED_SHA256_TOP_HALF;
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index fad1ec3..4171c7b 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -173,6 +173,7 @@
     StartGaiaAuthOffline();
 
     UserContext user_context(
+        user_manager::UserType::USER_TYPE_REGULAR,
         AccountId::FromUserEmailGaiaId(kTestUser, kGaiaId));
     user_context.SetKey(Key(kPassword));
     SetExpectedCredentials(user_context);
diff --git a/chrome/browser/chromeos/login/login_manager_test.cc b/chrome/browser/chromeos/login/login_manager_test.cc
index 5a1d61d9..5a076fc 100644
--- a/chrome/browser/chromeos/login/login_manager_test.cc
+++ b/chrome/browser/chromeos/login/login_manager_test.cc
@@ -52,7 +52,8 @@
 constexpr char kTestRefreshToken2[] = "fake-refresh-token-2";
 
 UserContext CreateUserContext(const AccountId& account_id) {
-  UserContext user_context(account_id);
+  UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                           account_id);
   user_context.SetKey(Key("password"));
   if (account_id.GetUserEmail() == LoginManagerTest::kEnterpriseUser1) {
     user_context.SetRefreshToken(kTestRefreshToken1);
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index fdd0c31..cbdfddc 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -749,7 +749,10 @@
                                             const std::string& key_label) {
   DCHECK_EQ(GetScreenType(), SIGNIN_SCREEN);
 
-  UserContext user_context(account_id);
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
+  UserContext user_context(*user);
   user_context.SetAuthFlow(UserContext::AUTH_FLOW_EASY_UNLOCK);
   user_context.SetKey(Key(secret));
   user_context.GetKey()->SetLabel(key_label);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 65561208..0123894 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1737,6 +1737,11 @@
   pending_user_sessions_.erase(account_id);
 
   // Check that this user is not logged in yet.
+
+  // TODO(alemate): Investigate whether this could be simplified by enforcing
+  // session restore to existing users only. Currently this breakes some tests
+  // (namely CrashRestoreComplexTest.RestoreSessionForThreeUsers), but
+  // it may be test-specific and could probably be changed.
   user_manager::UserList logged_in_users =
       user_manager::UserManager::Get()->GetLoggedInUsers();
   bool user_already_logged_in = false;
@@ -1751,7 +1756,12 @@
   DCHECK(!user_already_logged_in);
 
   if (!user_already_logged_in) {
-    UserContext user_context(account_id);
+    const user_manager::User* const user =
+        user_manager::UserManager::Get()->FindUser(account_id);
+    UserContext user_context =
+        user ? UserContext(*user)
+             : UserContext(user_manager::UserType::USER_TYPE_REGULAR,
+                           account_id);
     user_context.SetUserIDHash(user_id_hash);
     user_context.SetIsUsingOAuth(false);
 
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 03b0bc4c..1bacf692 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -336,7 +336,8 @@
       return false;
     }
 
-    UserContext user_context(account_id);
+    UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                             account_id);
     user_context.SetKey(Key(password));
     controller->Login(user_context, SigninSpecifics());
     content::WindowedNotificationObserver(
@@ -657,6 +658,7 @@
   ExistingUserController* const controller =
       ExistingUserController::current_controller();
   UserContext user_context(
+      user_manager::USER_TYPE_REGULAR,
       AccountId::FromUserEmailGaiaId(kTestEmail, kTestGaiaId));
   user_context.SetKey(Key(kTestAccountPassword));
   controller->Login(user_context, SigninSpecifics());
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
index ac2b215..33d83b0 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
@@ -205,7 +205,14 @@
 
   controller_.reset(new SupervisedUserCreationControllerNew(this, manager_id));
 
-  UserContext user_context(manager_id);
+  const user_manager::User* const manager =
+      user_manager::UserManager::Get()->FindUser(manager_id);
+  // Tests depend on the ability to create users on-demand, so we cannot
+  // require manager to exist here.
+  UserContext user_context =
+      manager
+          ? UserContext(*manager)
+          : UserContext(user_manager::UserType::USER_TYPE_REGULAR, manager_id);
   user_context.SetKey(Key(manager_password));
   ExistingUserController::current_controller()->Login(user_context,
                                                       SigninSpecifics());
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
index 7b729f37..652f1aa 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -215,7 +215,10 @@
 
   on_authenticated_ = std::move(callback);
 
-  UserContext user_context(account_id);
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
+  UserContext user_context(*user);
   user_context.SetKey(Key(chromeos::Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
                           std::string(), hashed_password));
   user_context.SetSyncPasswordData(sync_password_data);
diff --git a/chrome/browser/chromeos/policy/affiliation_test_helper.cc b/chrome/browser/chromeos/policy/affiliation_test_helper.cc
index 33b8883..cfe65d7 100644
--- a/chrome/browser/chromeos/policy/affiliation_test_helper.cc
+++ b/chrome/browser/chromeos/policy/affiliation_test_helper.cc
@@ -117,7 +117,8 @@
       chromeos::UserSessionManager::GetInstance());
   session_manager_test_api.SetShouldObtainTokenHandleInTests(false);
 
-  chromeos::UserContext user_context(account_id);
+  chromeos::UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
+                                     account_id);
   user_context.SetKey(chromeos::Key("password"));
   if (account_id.GetUserEmail() == kEnterpriseUserEmail) {
     user_context.SetRefreshToken(kFakeRefreshToken);
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
index 32168051..16a815e 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
@@ -244,7 +244,7 @@
   // must be false.
   const bool cannot_tell_if_policy_required =
       (requires_policy_user_property == ProfileRequiresPolicy::kUnknown) &&
-      !is_stub_user &&
+      !is_stub_user && !is_active_directory &&
       !command_line->HasSwitch(chromeos::switches::kProfileRequiresPolicy) &&
       !command_line->HasSwitch(
           chromeos::switches::kAllowFailedPolicyFetchForTest);
@@ -275,12 +275,13 @@
   // command-line flag (required for ephemeral users who are not persisted
   // in the known_user database).
   const bool policy_required =
-      !command_line->HasSwitch(
-          chromeos::switches::kAllowFailedPolicyFetchForTest) &&
-      ((requires_policy_user_property ==
-        ProfileRequiresPolicy::kPolicyRequired) ||
-       (command_line->GetSwitchValueASCII(
-            chromeos::switches::kProfileRequiresPolicy) == "true"));
+      is_active_directory ||
+      (!command_line->HasSwitch(
+           chromeos::switches::kAllowFailedPolicyFetchForTest) &&
+       ((requires_policy_user_property ==
+         ProfileRequiresPolicy::kPolicyRequired) ||
+        (command_line->GetSwitchValueASCII(
+             chromeos::switches::kProfileRequiresPolicy) == "true")));
 
   DCHECK(!(cannot_tell_if_policy_required && policy_required));
 
diff --git a/chrome/browser/chromeos/printing/printers_sync_bridge.cc b/chrome/browser/chromeos/printing/printers_sync_bridge.cc
index 67a40d4..6c4f1020 100644
--- a/chrome/browser/chromeos/printing/printers_sync_bridge.cc
+++ b/chrome/browser/chromeos/printing/printers_sync_bridge.cc
@@ -73,6 +73,7 @@
     store_->CommitWriteBatch(
         std::move(batch),
         base::BindOnce(&StoreProxy::OnCommit, weak_ptr_factory_.GetWeakPtr()));
+    owner_->NotifyPrintersUpdated();
   }
 
  private:
diff --git a/chrome/browser/component_updater/sth_set_component_installer.cc b/chrome/browser/component_updater/sth_set_component_installer.cc
index 2c88ad1..962ae51 100644
--- a/chrome/browser/component_updater/sth_set_component_installer.cc
+++ b/chrome/browser/component_updater/sth_set_component_installer.cc
@@ -22,14 +22,15 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/after_startup_task_utils.h"
-#include "chrome/browser/net/sth_distributor_provider.h"
-#include "components/certificate_transparency/sth_distributor.h"
 #include "components/certificate_transparency/sth_observer.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
 #include "crypto/sha2.h"
 #include "net/cert/ct_log_response_parser.h"
 #include "net/cert/signed_tree_head.h"
+#include "services/network/network_service.h"
+#include "services/service_manager/public/cpp/service.h"
 
 using component_updater::ComponentUpdateService;
 
@@ -42,27 +43,6 @@
       .Append(kSTHsDirName);
 }
 
-// Lives and dies on the UI thread, but proxies notifications to the IO
-// thread to notify the chrome_browser_net::GetGlobalSTHDistributor(), as
-// it always lives on the IO thread.
-class IOThreadSTHObserver : public certificate_transparency::STHObserver {
- public:
-  ~IOThreadSTHObserver() override = default;
-
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth) override {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(
-            [](const net::ct::SignedTreeHead& sth) {
-              certificate_transparency::STHObserver* observer =
-                  chrome_browser_net::GetGlobalSTHDistributor();
-              if (observer)
-                observer->NewSTHObserved(sth);
-            },
-            sth));
-  }
-};
-
 // Loads the STHs from |sths_path|, which contains a series of files that
 // are the in the form "[log_id].sth", where [log_id] is the hex-encoded ID
 // of the log, and the contents are JSON STH. Parsed STHs will be posted to
@@ -150,15 +130,15 @@
 
 const char kSTHSetFetcherManifestName[] = "Signed Tree Heads";
 
-STHSetComponentInstallerPolicy::STHSetComponentInstallerPolicy(
-    std::unique_ptr<certificate_transparency::STHObserver> sth_observer)
-    : sth_observer_(std::move(sth_observer)), weak_ptr_factory_(this) {}
+STHSetComponentInstallerPolicy::STHSetComponentInstallerPolicy()
+    : weak_ptr_factory_(this) {}
 
 STHSetComponentInstallerPolicy::~STHSetComponentInstallerPolicy() = default;
 
-void STHSetComponentInstallerPolicy::NewSTHObserved(
-    const net::ct::SignedTreeHead& sth) {
-  sth_observer_->NewSTHObserved(sth);
+void STHSetComponentInstallerPolicy::SetNetworkServiceForTesting(
+    network::mojom::NetworkService* network_service) {
+  DCHECK(network_service);
+  network_service_for_testing_ = network_service;
 }
 
 bool STHSetComponentInstallerPolicy::
@@ -166,6 +146,16 @@
   return false;
 }
 
+void STHSetComponentInstallerPolicy::OnSTHLoaded(
+    const net::ct::SignedTreeHead& sth) {
+  // TODO(rsleevi): https://crbug.com/840444 - Ensure the network service
+  // is notified of the STHs if it crashes/restarts.
+  network::mojom::NetworkService* network_service =
+      network_service_for_testing_ ? network_service_for_testing_
+                                   : content::GetNetworkService();
+  network_service->UpdateSignedTreeHead(sth);
+}
+
 // Public data is delivered via this component, no need for encryption.
 bool STHSetComponentInstallerPolicy::RequiresNetworkEncryption() const {
   return false;
@@ -195,7 +185,7 @@
       base::BindOnce(
           &LoadSTHsFromDisk, GetInstalledPath(install_dir),
           base::SequencedTaskRunnerHandle::Get(),
-          base::BindRepeating(&STHSetComponentInstallerPolicy::NewSTHObserved,
+          base::BindRepeating(&STHSetComponentInstallerPolicy::OnSTHLoaded,
                               weak_ptr_factory_.GetWeakPtr())));
 }
 
@@ -231,10 +221,8 @@
 void RegisterSTHSetComponent(ComponentUpdateService* cus,
                              const base::FilePath& user_data_dir) {
   DVLOG(1) << "Registering STH Set fetcher component.";
-
   auto installer = base::MakeRefCounted<ComponentInstaller>(
-      std::make_unique<STHSetComponentInstallerPolicy>(
-          std::make_unique<IOThreadSTHObserver>()));
+      std::make_unique<STHSetComponentInstallerPolicy>());
   installer->Register(cus, base::Closure());
 }
 
diff --git a/chrome/browser/component_updater/sth_set_component_installer.h b/chrome/browser/component_updater/sth_set_component_installer.h
index 4049b92..bf14080 100644
--- a/chrome/browser/component_updater/sth_set_component_installer.h
+++ b/chrome/browser/component_updater/sth_set_component_installer.h
@@ -15,16 +15,18 @@
 #include "base/memory/weak_ptr.h"
 #include "components/component_updater/component_installer.h"
 
-namespace certificate_transparency {
-class STHObserver;
-}  // namespace certificate_transparency
-
 namespace net {
 namespace ct {
 struct SignedTreeHead;
 }  // namespace ct
 }  // namespace net
 
+namespace network {
+namespace mojom {
+class NetworkService;
+}  // namespace mojom
+}  // namespace network
+
 namespace component_updater {
 
 class ComponentUpdateService;
@@ -41,15 +43,16 @@
 // persistence.
 class STHSetComponentInstallerPolicy : public ComponentInstallerPolicy {
  public:
-  // The |sth_observer| will be notified each time a new STH is observed.
-  explicit STHSetComponentInstallerPolicy(
-      std::unique_ptr<certificate_transparency::STHObserver> sth_observer);
+  STHSetComponentInstallerPolicy();
   ~STHSetComponentInstallerPolicy() override;
 
  private:
   friend class STHSetComponentInstallerTest;
+  void SetNetworkServiceForTesting(
+      network::mojom::NetworkService* network_service);
 
-  void NewSTHObserved(const net::ct::SignedTreeHead& sth);
+  // Indicates that a new STH has been loaded.
+  void OnSTHLoaded(const net::ct::SignedTreeHead& sth);
 
   // ComponentInstallerPolicy implementation.
   bool SupportsGroupPolicyEnabledComponentUpdates() const override;
@@ -69,7 +72,7 @@
   update_client::InstallerAttributes GetInstallerAttributes() const override;
   std::vector<std::string> GetMimeTypes() const override;
 
-  std::unique_ptr<certificate_transparency::STHObserver> sth_observer_;
+  network::mojom::NetworkService* network_service_for_testing_ = nullptr;
 
   base::WeakPtrFactory<STHSetComponentInstallerPolicy> weak_ptr_factory_;
 
diff --git a/chrome/browser/component_updater/sth_set_component_installer_unittest.cc b/chrome/browser/component_updater/sth_set_component_installer_unittest.cc
index 62793cfe..67bb1e7 100644
--- a/chrome/browser/component_updater/sth_set_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/sth_set_component_installer_unittest.cc
@@ -19,10 +19,12 @@
 #include "base/version.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "components/certificate_transparency/sth_observer.h"
+#include "components/certificate_transparency/sth_reporter.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/cert/signed_tree_head.h"
 #include "net/test/ct_test_util.h"
 #include "services/data_decoder/public/cpp/testing_json_parser.h"
+#include "services/network/network_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -39,8 +41,14 @@
 
 class STHSetComponentInstallerTest : public PlatformTest {
  public:
-  STHSetComponentInstallerTest() {
+  STHSetComponentInstallerTest() : network_service_(nullptr) {
     AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting();
+
+    network_service_.sth_reporter()->RegisterObserver(&observer_);
+  }
+
+  ~STHSetComponentInstallerTest() override {
+    network_service_.sth_reporter()->UnregisterObserver(&observer_);
   }
 
   void SetUp() override {
@@ -48,11 +56,8 @@
 
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
 
-    std::unique_ptr<StoringSTHObserver> observer =
-        std::make_unique<StoringSTHObserver>();
-    observer_ = observer.get();
-    policy_ =
-        std::make_unique<STHSetComponentInstallerPolicy>(std::move(observer));
+    policy_ = std::make_unique<STHSetComponentInstallerPolicy>();
+    policy_->SetNetworkServiceForTesting(&network_service_);
   }
 
   void WriteSTHToFile(const std::string& sth_json,
@@ -87,10 +92,11 @@
  protected:
   content::TestBrowserThreadBundle thread_bundle_;
 
+  network::NetworkService network_service_;
+  StoringSTHObserver observer_;
+
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<STHSetComponentInstallerPolicy> policy_;
-  // Note that |observer_| is owned by |policy_|.
-  StoringSTHObserver* observer_ = nullptr;
   data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
 
  private:
@@ -114,15 +120,15 @@
 
   LoadSTHs(manifest, sths_dir);
 
-  EXPECT_EQ(2u, observer_->sths.size());
+  EXPECT_EQ(2u, observer_.sths.size());
 
   const std::string first_log_id("abc");
-  ASSERT_TRUE(observer_->sths.find(first_log_id) != observer_->sths.end());
-  const net::ct::SignedTreeHead& first_sth(observer_->sths[first_log_id]);
+  ASSERT_TRUE(observer_.sths.find(first_log_id) != observer_.sths.end());
+  const net::ct::SignedTreeHead& first_sth(observer_.sths[first_log_id]);
   EXPECT_EQ(21u, first_sth.tree_size);
 
   const std::string second_log_id("a\00d", 3);
-  ASSERT_TRUE(observer_->sths.find(second_log_id) != observer_->sths.end());
+  ASSERT_TRUE(observer_.sths.find(second_log_id) != observer_.sths.end());
 }
 
 // Does not notify of invalid STH JSON.
@@ -136,7 +142,7 @@
   WriteSTHToFile(std::string("{invalid json}"), invalid_sth);
 
   LoadSTHs(manifest, sths_dir);
-  EXPECT_EQ(0u, observer_->sths.size());
+  EXPECT_EQ(0u, observer_.sths.size());
 }
 
 // Does not notify of valid JSON but in a file not hex-encoded log id.
@@ -151,7 +157,7 @@
   WriteSTHToFile(net::ct::GetSampleSTHAsJson(), not_hex_sth_file);
 
   LoadSTHs(manifest, sths_dir);
-  EXPECT_EQ(0u, observer_->sths.size());
+  EXPECT_EQ(0u, observer_.sths.size());
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index b21faa7..075b517 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -2178,6 +2178,13 @@
   DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
 }
 
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DisposeEmptyBrowserContext) {
+  window_ =
+      DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
+  RunTestMethod("testDisposeEmptyBrowserContext");
+  DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
+}
+
 IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsSanityTest, InspectElement) {
   GURL url(embedded_test_server()->GetURL("a.com", "/devtools/oopif.html"));
   GURL iframe_url(
diff --git a/chrome/browser/devtools/inspector_protocol_config.json b/chrome/browser/devtools/inspector_protocol_config.json
index 96b580a7..38dfa64c 100644
--- a/chrome/browser/devtools/inspector_protocol_config.json
+++ b/chrome/browser/devtools/inspector_protocol_config.json
@@ -18,7 +18,8 @@
             },
             {
                 "domain": "Target",
-                "include": [ "setRemoteLocations", "createBrowserContext", "createTarget", "disposeBrowserContext" ],
+                "include": [ "setRemoteLocations", "createBrowserContext", "getBrowserContexts", "createTarget", "disposeBrowserContext" ],
+                "async": ["disposeBrowserContext"],
                 "include_events": []
             },
             {
diff --git a/chrome/browser/devtools/protocol/target_handler.cc b/chrome/browser/devtools/protocol/target_handler.cc
index cb544fe..f5c6272 100644
--- a/chrome/browser/devtools/protocol/target_handler.cc
+++ b/chrome/browser/devtools/protocol/target_handler.cc
@@ -11,8 +11,146 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "content/public/browser/devtools_agent_host.h"
 
-TargetHandler::TargetHandler(protocol::UberDispatcher* dispatcher)
-    : weak_factory_(this) {
+namespace {
+
+class DevToolsBrowserContextManager : public BrowserListObserver {
+ public:
+  DevToolsBrowserContextManager();
+  protocol::Response CreateBrowserContext(std::string* out_context_id);
+  protocol::Response GetBrowserContexts(
+      std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids);
+  Profile* GetBrowserContext(const std::string& context_id);
+  void DisposeBrowserContext(
+      const std::string& context_id,
+      std::unique_ptr<TargetHandler::DisposeBrowserContextCallback> callback);
+
+ private:
+  void OnOriginalProfileDestroyed(Profile* profile);
+
+  void OnBrowserRemoved(Browser* browser) override;
+
+  base::flat_map<
+      std::string,
+      std::unique_ptr<IndependentOTRProfileManager::OTRProfileRegistration>>
+      registrations_;
+  base::flat_map<std::string,
+                 std::unique_ptr<TargetHandler::DisposeBrowserContextCallback>>
+      pending_context_disposals_;
+
+  base::WeakPtrFactory<DevToolsBrowserContextManager> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(DevToolsBrowserContextManager);
+};
+
+DevToolsBrowserContextManager::DevToolsBrowserContextManager()
+    : weak_factory_(this) {}
+
+Profile* DevToolsBrowserContextManager::GetBrowserContext(
+    const std::string& context_id) {
+  auto it = registrations_.find(context_id);
+  return it == registrations_.end() ? nullptr : it->second->profile();
+}
+
+protocol::Response DevToolsBrowserContextManager::CreateBrowserContext(
+    std::string* out_context_id) {
+  Profile* original_profile =
+      ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
+
+  auto registration =
+      IndependentOTRProfileManager::GetInstance()->CreateFromOriginalProfile(
+          original_profile,
+          base::BindOnce(
+              &DevToolsBrowserContextManager::OnOriginalProfileDestroyed,
+              weak_factory_.GetWeakPtr()));
+  *out_context_id = registration->profile()->UniqueId();
+  registrations_[*out_context_id] = std::move(registration);
+  return protocol::Response::OK();
+}
+
+protocol::Response DevToolsBrowserContextManager::GetBrowserContexts(
+    std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids) {
+  *browser_context_ids = std::make_unique<protocol::Array<protocol::String>>();
+  for (const auto& registration_pair : registrations_) {
+    (*browser_context_ids)
+        ->addItem(registration_pair.second->profile()->UniqueId());
+  }
+  return protocol::Response::OK();
+}
+
+void DevToolsBrowserContextManager::DisposeBrowserContext(
+    const std::string& context_id,
+    std::unique_ptr<TargetHandler::DisposeBrowserContextCallback> callback) {
+  if (pending_context_disposals_.find(context_id) !=
+      pending_context_disposals_.end()) {
+    callback->sendFailure(protocol::Response::Error(
+        "Disposal of browser context " + context_id + " is already pending"));
+    return;
+  }
+  auto it = registrations_.find(context_id);
+  if (it == registrations_.end()) {
+    callback->sendFailure(protocol::Response::InvalidParams(
+        "Failed to find browser context with id " + context_id));
+    return;
+  }
+
+  Profile* profile = it->second->profile();
+  bool has_opened_browser = false;
+  for (auto* opened_browser : *BrowserList::GetInstance()) {
+    if (opened_browser->profile() == profile) {
+      has_opened_browser = true;
+      break;
+    }
+  }
+
+  // If no browsers are opened - dispose right away.
+  if (!has_opened_browser) {
+    registrations_.erase(it);
+    callback->sendSuccess();
+    return;
+  }
+
+  if (pending_context_disposals_.empty())
+    BrowserList::AddObserver(this);
+
+  pending_context_disposals_[context_id] = std::move(callback);
+  BrowserList::CloseAllBrowsersWithIncognitoProfile(
+      profile, base::DoNothing(), base::DoNothing(),
+      true /* skip_beforeunload */);
+}
+
+void DevToolsBrowserContextManager::OnOriginalProfileDestroyed(
+    Profile* profile) {
+  base::EraseIf(registrations_, [&profile](const auto& it) {
+    return it.second->profile()->GetOriginalProfile() == profile;
+  });
+}
+
+void DevToolsBrowserContextManager::OnBrowserRemoved(Browser* browser) {
+  std::string context_id = browser->profile()->UniqueId();
+  auto pending_disposal = pending_context_disposals_.find(context_id);
+  if (pending_disposal == pending_context_disposals_.end())
+    return;
+  for (auto* opened_browser : *BrowserList::GetInstance()) {
+    if (opened_browser->profile() == browser->profile())
+      return;
+  }
+  auto it = registrations_.find(context_id);
+  // We cannot delete immediately here: the profile might still be referenced
+  // during the browser tier-down process.
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                  it->second.release());
+  registrations_.erase(it);
+  pending_disposal->second->sendSuccess();
+  pending_context_disposals_.erase(pending_disposal);
+  if (pending_context_disposals_.empty())
+    BrowserList::RemoveObserver(this);
+}
+
+base::LazyInstance<DevToolsBrowserContextManager>::Leaky
+    g_devtools_browser_context_manager;
+
+}  // namespace
+
+TargetHandler::TargetHandler(protocol::UberDispatcher* dispatcher) {
   protocol::Target::Dispatcher::wire(dispatcher, this);
 }
 
@@ -21,7 +159,6 @@
       ChromeDevToolsManagerDelegate::GetInstance();
   if (delegate)
     delegate->UpdateDeviceDiscovery();
-  registrations_.clear();
 }
 
 protocol::Response TargetHandler::SetRemoteLocations(
@@ -54,12 +191,12 @@
   Profile* profile = ProfileManager::GetActiveUserProfile();
   if (browser_context_id.isJust()) {
     std::string profile_id = browser_context_id.fromJust();
-    auto it = registrations_.find(profile_id);
-    if (it == registrations_.end()) {
+    profile =
+        g_devtools_browser_context_manager.Get().GetBrowserContext(profile_id);
+    if (!profile) {
       return protocol::Response::Error(
           "Failed to find browser context with id " + profile_id);
     }
-    profile = it->second->profile();
   }
 
   NavigateParams params(profile, GURL(url), ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
@@ -92,42 +229,20 @@
 
 protocol::Response TargetHandler::CreateBrowserContext(
     std::string* out_context_id) {
-  Profile* original_profile =
-      ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
-
-  auto registration =
-      IndependentOTRProfileManager::GetInstance()->CreateFromOriginalProfile(
-          original_profile,
-          base::BindOnce(&TargetHandler::OnOriginalProfileDestroyed,
-                         weak_factory_.GetWeakPtr()));
-  *out_context_id = registration->profile()->UniqueId();
-  registrations_[*out_context_id] = std::move(registration);
-  return protocol::Response::OK();
+  return g_devtools_browser_context_manager.Get().CreateBrowserContext(
+      out_context_id);
 }
 
-void TargetHandler::OnOriginalProfileDestroyed(Profile* profile) {
-  base::EraseIf(registrations_, [&profile](const auto& it) {
-    return it.second->profile()->GetOriginalProfile() == profile;
-  });
+protocol::Response TargetHandler::GetBrowserContexts(
+    std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids) {
+  return g_devtools_browser_context_manager.Get().GetBrowserContexts(
+      browser_context_ids);
 }
 
-protocol::Response TargetHandler::DisposeBrowserContext(
+void TargetHandler::DisposeBrowserContext(
     const std::string& context_id,
-    bool* out_success) {
-  auto it = registrations_.find(context_id);
-  if (it == registrations_.end()) {
-    return protocol::Response::Error("Failed to find browser context with id " +
-                                     context_id);
-  }
-  Profile* profile = it->second->profile();
-  for (auto* browser : *BrowserList::GetInstance()) {
-    if (browser->profile() == profile &&
-        !browser->IsAttemptingToCloseBrowser()) {
-      *out_success = false;
-      return protocol::Response::OK();
-    }
-  }
-  registrations_.erase(it);
-  *out_success = true;
-  return protocol::Response::OK();
+    std::unique_ptr<DisposeBrowserContextCallback> callback) {
+  g_devtools_browser_context_manager.Get().DisposeBrowserContext(
+      context_id, std::move(callback));
 }
+
diff --git a/chrome/browser/devtools/protocol/target_handler.h b/chrome/browser/devtools/protocol/target_handler.h
index bfff3d0..e9933b09 100644
--- a/chrome/browser/devtools/protocol/target_handler.h
+++ b/chrome/browser/devtools/protocol/target_handler.h
@@ -11,6 +11,8 @@
 #include "chrome/browser/devtools/protocol/forward.h"
 #include "chrome/browser/devtools/protocol/target.h"
 #include "chrome/browser/media/router/presentation/independent_otr_profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list_observer.h"
 #include "net/base/host_port_pair.h"
 
 using RemoteLocations = std::set<net::HostPortPair>;
@@ -34,20 +36,16 @@
       protocol::Maybe<std::string> browser_context_id,
       protocol::Maybe<bool> enable_begin_frame_control,
       std::string* out_target_id) override;
-  protocol::Response DisposeBrowserContext(const std::string& context_id,
-                                           bool* out_success) override;
+  protocol::Response GetBrowserContexts(
+      std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids)
+      override;
+  void DisposeBrowserContext(
+      const std::string& context_id,
+      std::unique_ptr<DisposeBrowserContextCallback> callback) override;
 
  private:
-  void OnOriginalProfileDestroyed(Profile* profile);
-
-  base::flat_map<
-      std::string,
-      std::unique_ptr<IndependentOTRProfileManager::OTRProfileRegistration>>
-      registrations_;
   RemoteLocations remote_locations_;
 
-  base::WeakPtrFactory<TargetHandler> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(TargetHandler);
 };
 
diff --git a/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc b/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
index 752600ca..2289c0a 100644
--- a/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
+++ b/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
@@ -181,10 +181,8 @@
   {
     scoped_refptr<Extension> auto_launch_kiosk_app(
         ExtensionBuilder("Test", ExtensionBuilder::Type::PLATFORM_APP)
-            .MergeManifest(DictionaryBuilder()
-                               .Set("kiosk_enabled", true)
-                               .Set("kiosk_only", true)
-                               .Build())
+            .SetManifestKey("kiosk_enabled", true)
+            .SetManifestKey("kiosk_only", true)
             .Build());
     user_manager_->AddKioskAppUser(
         AccountId::FromUserEmail(auto_launch_kiosk_app->id()));
@@ -211,10 +209,8 @@
   // receiving approval from the user.
   scoped_refptr<Extension> manual_launch_kiosk_app(
       ExtensionBuilder("Test", ExtensionBuilder::Type::PLATFORM_APP)
-          .MergeManifest(DictionaryBuilder()
-                             .Set("kiosk_enabled", true)
-                             .Set("kiosk_only", true)
-                             .Build())
+          .SetManifestKey("kiosk_enabled", true)
+          .SetManifestKey("kiosk_only", true)
           .Build());
   user_manager::User* const manual_kiosk_app_user =
       user_manager_->AddKioskAppUser(
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 9c24f96..8b5c6e49 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -1955,14 +1955,11 @@
 
   scoped_refptr<Extension> CreateTestExtension(const std::string& id) {
     return ExtensionBuilder("Test")
-        .MergeManifest(
-            DictionaryBuilder()
-                .Set("oauth2",
-                     DictionaryBuilder()
-                         .Set("client_id", "clientId")
-                         .Set("scopes", ListBuilder().Append("scope1").Build())
-                         .Build())
-                .Build())
+        .SetManifestKey(
+            "oauth2", DictionaryBuilder()
+                          .Set("client_id", "clientId")
+                          .Set("scopes", ListBuilder().Append("scope1").Build())
+                          .Build())
         .SetID(id)
         .Build();
   }
diff --git a/chrome/browser/extensions/api/management/management_browsertest.cc b/chrome/browser/extensions/api/management/management_browsertest.cc
index 47481fc..adba1640 100644
--- a/chrome/browser/extensions/api/management/management_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_browsertest.cc
@@ -59,7 +59,7 @@
 
 }  // namespace
 
-class ExtensionManagementTest : public ExtensionBrowserTest {
+class ExtensionManagementTest : public extensions::ExtensionBrowserTest {
  public:
   void SetUpInProcessBrowserTestFixture() override {
     EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
diff --git a/chrome/browser/extensions/background_app_browsertest.cc b/chrome/browser/extensions/background_app_browsertest.cc
index f704c2b..e4647ab 100644
--- a/chrome/browser/extensions/background_app_browsertest.cc
+++ b/chrome/browser/extensions/background_app_browsertest.cc
@@ -44,7 +44,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestBackgroundModeManager);
 };
 
-class BackgroundAppBrowserTest: public ExtensionBrowserTest {};
+using BackgroundAppBrowserTest = extensions::ExtensionBrowserTest;
 
 // Tests that if we reload a background app, we don't get a popup bubble
 // telling us that a new background app has been installed.
diff --git a/chrome/browser/extensions/chrome_app_api_browsertest.cc b/chrome/browser/extensions/chrome_app_api_browsertest.cc
index 978739c6..fc02b55 100644
--- a/chrome/browser/extensions/chrome_app_api_browsertest.cc
+++ b/chrome/browser/extensions/chrome_app_api_browsertest.cc
@@ -25,9 +25,9 @@
 
 using extensions::Extension;
 
-class ChromeAppAPITest : public ExtensionBrowserTest {
+class ChromeAppAPITest : public extensions::ExtensionBrowserTest {
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
     ASSERT_TRUE(embedded_test_server()->Start());
   }
diff --git a/chrome/browser/extensions/chrome_ui_overrides_browsertest.cc b/chrome/browser/extensions/chrome_ui_overrides_browsertest.cc
index 0065195..62b1b17 100644
--- a/chrome/browser/extensions/chrome_ui_overrides_browsertest.cc
+++ b/chrome/browser/extensions/chrome_ui_overrides_browsertest.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/common/url_constants.h"
 
-class ChromeUIOverridesBrowserTest : public ExtensionBrowserTest {};
+using ChromeUIOverridesBrowserTest = extensions::ExtensionBrowserTest;
 
 IN_PROC_BROWSER_TEST_F(ChromeUIOverridesBrowserTest,
                        BookmarkShortcutOverrides) {
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index c8e7c07..91b49fe 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -43,6 +43,8 @@
 
 namespace {
 
+using extensions::ExtensionBrowserTest;
+
 const char kTestCustomArg[] = "customArg";
 const char kTestDataDirectory[] = "testDataDirectory";
 const char kTestWebSocketPort[] = "testWebSocketPort";
diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h
index e3551ec..e3f7ebb 100644
--- a/chrome/browser/extensions/extension_apitest.h
+++ b/chrome/browser/extensions/extension_apitest.h
@@ -29,7 +29,7 @@
 //     chrome.test.fail
 // (4) Verify expected browser state.
 // TODO(erikkay): There should also be a way to drive events in these tests.
-class ExtensionApiTest : public ExtensionBrowserTest {
+class ExtensionApiTest : public extensions::ExtensionBrowserTest {
  public:
   // Flags used to configure how the tests are run.
   // TODO(aa): Many of these are dupes of ExtensionBrowserTest::Flags. Combine
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index a9cbd6f6..8c89851 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -75,21 +75,16 @@
 #include "chromeos/chromeos_switches.h"
 #endif
 
-using extensions::Extension;
-using extensions::ExtensionCreator;
-using extensions::ExtensionRegistry;
-using extensions::FeatureSwitch;
-using extensions::Manifest;
-using extensions::ScopedTestDialogAutoConfirm;
+namespace extensions {
 
 namespace {
 
 // Maps all chrome-extension://<id>/_test_resources/foo requests to
 // chrome/test/data/extensions/foo. This is what allows us to share code between
 // tests without needing to duplicate files in each extension.
-void ExtensionProtocolTestHandler(const base::FilePath& test_dir_root,
-                                  base::FilePath* directory_path,
-                                  base::FilePath* relative_path) {
+void ExtensionProtocolTestResourcesHandler(const base::FilePath& test_dir_root,
+                                           base::FilePath* directory_path,
+                                           base::FilePath* relative_path) {
   // Only map paths that begin with _test_resources.
   if (!base::FilePath(FILE_PATH_LITERAL("_test_resources"))
            .IsParent(*relative_path)) {
@@ -156,7 +151,7 @@
 
 // static
 const Extension* ExtensionBrowserTest::GetExtensionByPath(
-    const extensions::ExtensionSet& extensions,
+    const ExtensionSet& extensions,
     const base::FilePath& path) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
@@ -170,7 +165,7 @@
 }
 
 void ExtensionBrowserTest::SetUp() {
-  test_extension_cache_.reset(new extensions::ExtensionCacheFake());
+  test_extension_cache_.reset(new ExtensionCacheFake());
   InProcessBrowserTest::SetUp();
 }
 
@@ -184,12 +179,12 @@
 
   if (!ShouldEnableContentVerification()) {
     ignore_content_verification_.reset(
-        new extensions::ScopedIgnoreContentVerifierForTest());
+        new ScopedIgnoreContentVerifierForTest());
   }
 
   if (!ShouldEnableInstallVerification()) {
     ignore_install_verification_.reset(
-        new extensions::ScopedInstallVerifierBypassForTest());
+        new ScopedInstallVerifierBypassForTest());
   }
 
 #if defined(OS_CHROMEOS)
@@ -205,8 +200,7 @@
 }
 
 void ExtensionBrowserTest::SetUpOnMainThread() {
-  observer_.reset(
-      new extensions::ChromeExtensionTestNotificationObserver(browser()));
+  observer_.reset(new ChromeExtensionTestNotificationObserver(browser()));
   if (extension_service()->updater()) {
     extension_service()->updater()->SetExtensionCacheForTesting(
         test_extension_cache_.get());
@@ -219,14 +213,14 @@
   base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
   test_root_path = test_root_path.AppendASCII("extensions");
   test_protocol_handler_ =
-      base::Bind(&ExtensionProtocolTestHandler, test_root_path);
-  extensions::SetExtensionProtocolTestHandler(&test_protocol_handler_);
+      base::Bind(&ExtensionProtocolTestResourcesHandler, test_root_path);
+  SetExtensionProtocolTestHandler(&test_protocol_handler_);
 }
 
 void ExtensionBrowserTest::TearDownOnMainThread() {
   ExtensionMessageBubbleFactory::set_override_for_tests(
       ExtensionMessageBubbleFactory::NO_OVERRIDE);
-  extensions::SetExtensionProtocolTestHandler(nullptr);
+  SetExtensionProtocolTestHandler(nullptr);
 }
 
 const Extension* ExtensionBrowserTest::LoadExtension(
@@ -249,7 +243,7 @@
     const base::FilePath& path,
     int flags,
     const std::string& install_param) {
-  extensions::ChromeTestExtensionLoader loader(profile());
+  ChromeTestExtensionLoader loader(profile());
   loader.set_require_modern_manifest_version(
       (flags & kFlagAllowOldManifestVersions) == 0);
   loader.set_ignore_manifest_warnings(
@@ -266,8 +260,8 @@
 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
     const base::FilePath& path,
     const base::FilePath::CharType* manifest_relative_path) {
-  ExtensionService* service = extensions::ExtensionSystem::Get(
-      profile())->extension_service();
+  ExtensionService* service =
+      ExtensionSystem::Get(profile())->extension_service();
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
 
   base::ScopedAllowBlockingForTesting allow_blocking;
@@ -288,8 +282,7 @@
 
 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
     const base::FilePath& path) {
-  return LoadExtensionAsComponentWithManifest(path,
-                                              extensions::kManifestFilename);
+  return LoadExtensionAsComponentWithManifest(path, kManifestFilename);
 }
 
 const Extension* ExtensionBrowserTest::LoadAndLaunchApp(
@@ -299,9 +292,8 @@
   content::WindowedNotificationObserver app_loaded_observer(
       content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
       content::NotificationService::AllSources());
-  AppLaunchParams params(profile(), app, extensions::LAUNCH_CONTAINER_NONE,
-                         WindowOpenDisposition::NEW_WINDOW,
-                         extensions::SOURCE_TEST);
+  AppLaunchParams params(profile(), app, LAUNCH_CONTAINER_NONE,
+                         WindowOpenDisposition::NEW_WINDOW, SOURCE_TEST);
   params.command_line = *base::CommandLine::ForCurrentProcess();
   OpenApplication(params);
   app_loaded_observer.Wait();
@@ -309,9 +301,8 @@
   return app;
 }
 
-Browser* ExtensionBrowserTest::LaunchAppBrowser(
-    const extensions::Extension* extension) {
-  return extensions::browsertest_util::LaunchAppBrowser(profile(), extension);
+Browser* ExtensionBrowserTest::LaunchAppBrowser(const Extension* extension) {
+  return browsertest_util::LaunchAppBrowser(profile(), extension);
 }
 
 base::FilePath ExtensionBrowserTest::PackExtension(
@@ -385,8 +376,7 @@
 
 const Extension* ExtensionBrowserTest::InstallBookmarkApp(
     WebApplicationInfo info) {
-  return extensions::browsertest_util::InstallBookmarkApp(profile(),
-                                                          std::move(info));
+  return browsertest_util::InstallBookmarkApp(profile(), std::move(info));
 }
 
 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
@@ -441,7 +431,7 @@
     bool install_immediately,
     bool grant_permissions) {
   ExtensionService* service =
-      extensions::ExtensionSystem::Get(profile())->extension_service();
+      ExtensionSystem::Get(profile())->extension_service();
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
   size_t num_before = registry->enabled_extensions().size();
 
@@ -472,8 +462,8 @@
       install_ui.reset(new ExtensionInstallPrompt(
          browser->tab_strip_model()->GetActiveWebContents()));
     }
-    scoped_refptr<extensions::CrxInstaller> installer(
-        extensions::CrxInstaller::Create(service, std::move(install_ui)));
+    scoped_refptr<CrxInstaller> installer(
+        CrxInstaller::Create(service, std::move(install_ui)));
     installer->set_expected_id(id);
     installer->set_creation_flags(creation_flags);
     installer->set_install_source(install_source);
@@ -481,12 +471,11 @@
     installer->set_allow_silent_install(grant_permissions);
     if (!installer->is_gallery_install()) {
       installer->set_off_store_install_allow_reason(
-          extensions::CrxInstaller::OffStoreInstallAllowedInTest);
+          CrxInstaller::OffStoreInstallAllowedInTest);
     }
 
-    observer_->Watch(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::Source<extensions::CrxInstaller>(installer.get()));
+    observer_->Watch(NOTIFICATION_CRX_INSTALLER_DONE,
+                     content::Source<CrxInstaller>(installer.get()));
 
     installer->InstallCrx(crx_path);
 
@@ -506,7 +495,7 @@
 
     VLOG(1) << "Errors follow:";
     const std::vector<base::string16>* errors =
-        extensions::LoadErrorReporter::GetInstance()->GetErrors();
+        LoadErrorReporter::GetInstance()->GetErrors();
     for (std::vector<base::string16>::const_iterator iter = errors->begin();
          iter != errors->end(); ++iter)
       VLOG(1) << *iter;
@@ -520,14 +509,12 @@
 }
 
 void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(profile());
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
   const Extension* extension = registry->GetInstalledExtension(extension_id);
   ASSERT_TRUE(extension);
-  extensions::TestExtensionRegistryObserver observer(registry, extension_id);
-  extensions::ExtensionSystem::Get(profile())
-      ->extension_service()
-      ->ReloadExtension(extension_id);
+  TestExtensionRegistryObserver observer(registry, extension_id);
+  ExtensionSystem::Get(profile())->extension_service()->ReloadExtension(
+      extension_id);
   observer.WaitForExtensionLoaded();
 
   // We need to let other ExtensionRegistryObservers handle the extension load
@@ -539,30 +526,26 @@
 }
 
 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
-  ExtensionService* service = extensions::ExtensionSystem::Get(
-      profile())->extension_service();
-  service->UnloadExtension(extension_id,
-                           extensions::UnloadedExtensionReason::DISABLE);
+  ExtensionService* service =
+      ExtensionSystem::Get(profile())->extension_service();
+  service->UnloadExtension(extension_id, UnloadedExtensionReason::DISABLE);
 }
 
 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
-  ExtensionService* service = extensions::ExtensionSystem::Get(
-      profile())->extension_service();
-  service->UninstallExtension(extension_id,
-                              extensions::UNINSTALL_REASON_FOR_TESTING,
-                              NULL);
+  ExtensionService* service =
+      ExtensionSystem::Get(profile())->extension_service();
+  service->UninstallExtension(extension_id, UNINSTALL_REASON_FOR_TESTING, NULL);
 }
 
 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
-  ExtensionService* service = extensions::ExtensionSystem::Get(
-      profile())->extension_service();
-  service->DisableExtension(extension_id,
-                            extensions::disable_reason::DISABLE_USER_ACTION);
+  ExtensionService* service =
+      ExtensionSystem::Get(profile())->extension_service();
+  service->DisableExtension(extension_id, disable_reason::DISABLE_USER_ACTION);
 }
 
 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
-  ExtensionService* service = extensions::ExtensionSystem::Get(
-      profile())->extension_service();
+  ExtensionService* service =
+      ExtensionSystem::Get(profile())->extension_service();
   service->EnableExtension(extension_id);
 }
 
@@ -624,13 +607,12 @@
   EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
 }
 
-extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
-    extensions::ProcessManager* manager,
-    const std::string& path,
-    int expected_hosts) {
-  extensions::ExtensionHost* result_host = nullptr;
+ExtensionHost* ExtensionBrowserTest::FindHostWithPath(ProcessManager* manager,
+                                                      const std::string& path,
+                                                      int expected_hosts) {
+  ExtensionHost* result_host = nullptr;
   int num_hosts = 0;
-  for (extensions::ExtensionHost* host : manager->background_hosts()) {
+  for (ExtensionHost* host : manager->background_hosts()) {
     if (host->GetURL().path() == path) {
       EXPECT_FALSE(result_host);
       result_host = host;
@@ -644,13 +626,15 @@
 std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
     const std::string& extension_id,
     const std::string& script) {
-  return extensions::browsertest_util::ExecuteScriptInBackgroundPage(
-      profile(), extension_id, script);
+  return browsertest_util::ExecuteScriptInBackgroundPage(profile(),
+                                                         extension_id, script);
 }
 
 bool ExtensionBrowserTest::ExecuteScriptInBackgroundPageNoWait(
     const std::string& extension_id,
     const std::string& script) {
-  return extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait(
+  return browsertest_util::ExecuteScriptInBackgroundPageNoWait(
       profile(), extension_id, script);
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 0b2396a8..26791a4 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -36,7 +36,6 @@
 class ExtensionCacheFake;
 class ExtensionSet;
 class ProcessManager;
-}
 
 // Base class for extension browser tests. Provides utilities for loading,
 // unloading, and installing extensions.
@@ -66,7 +65,7 @@
 
   // Useful accessors.
   ExtensionService* extension_service() {
-    return extensions::ExtensionSystem::Get(profile())->extension_service();
+    return ExtensionSystem::Get(profile())->extension_service();
   }
 
   const std::string& last_loaded_extension_id() {
@@ -88,9 +87,8 @@
   // about install verification.
   virtual bool ShouldEnableInstallVerification();
 
-  static const extensions::Extension* GetExtensionByPath(
-      const extensions::ExtensionSet& extensions,
-      const base::FilePath& path);
+  static const Extension* GetExtensionByPath(const ExtensionSet& extensions,
+                                             const base::FilePath& path);
 
   // InProcessBrowserTest
   void SetUp() override;
@@ -98,20 +96,19 @@
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
-  const extensions::Extension* LoadExtension(const base::FilePath& path);
+  const Extension* LoadExtension(const base::FilePath& path);
 
   // Load extension and enable it in incognito mode.
-  const extensions::Extension* LoadExtensionIncognito(
-      const base::FilePath& path);
+  const Extension* LoadExtensionIncognito(const base::FilePath& path);
 
   // Load extension from the |path| folder. |flags| is bit mask of values from
   // |Flags| enum.
-  const extensions::Extension* LoadExtensionWithFlags(
-      const base::FilePath& path, int flags);
+  const Extension* LoadExtensionWithFlags(const base::FilePath& path,
+                                          int flags);
 
   // Same as above, but sets the installation parameter to the extension
   // preferences.
-  const extensions::Extension* LoadExtensionWithInstallParam(
+  const Extension* LoadExtensionWithInstallParam(
       const base::FilePath& path,
       int flags,
       const std::string& install_param);
@@ -119,21 +116,20 @@
   // Loads unpacked extension from |path| with manifest |manifest_relative_path|
   // and imitates that it is a component extension.
   // |manifest_relative_path| is relative to |path|.
-  const extensions::Extension* LoadExtensionAsComponentWithManifest(
+  const Extension* LoadExtensionAsComponentWithManifest(
       const base::FilePath& path,
       const base::FilePath::CharType* manifest_relative_path);
 
   // Loads unpacked extension from |path| and imitates that it is a component
   // extension. Equivalent to
-  // LoadExtensionAsComponentWithManifest(path, extensions::kManifestFilename).
-  const extensions::Extension* LoadExtensionAsComponent(
-      const base::FilePath& path);
+  // LoadExtensionAsComponentWithManifest(path, kManifestFilename).
+  const Extension* LoadExtensionAsComponent(const base::FilePath& path);
 
   // Loads and launches the app from |path|, and returns it.
-  const extensions::Extension* LoadAndLaunchApp(const base::FilePath& path);
+  const Extension* LoadAndLaunchApp(const base::FilePath& path);
 
   // Launches |extension| as a window and returns the browser.
-  Browser* LaunchAppBrowser(const extensions::Extension* extension);
+  Browser* LaunchAppBrowser(const Extension* extension);
 
   // Pack the extension in |dir_path| into a crx file and return its path.
   // Return an empty FilePath if there were errors.
@@ -152,18 +148,17 @@
   // disabled, if negative).
   // 1 means you expect a new install, 0 means you expect an upgrade, -1 means
   // you expect a failed upgrade.
-  const extensions::Extension* InstallExtension(const base::FilePath& path,
-                                                int expected_change) {
+  const Extension* InstallExtension(const base::FilePath& path,
+                                    int expected_change) {
     return InstallOrUpdateExtension(
         std::string(), path, INSTALL_UI_TYPE_NONE, expected_change);
   }
 
   // Same as above, but an install source other than Manifest::INTERNAL can be
   // specified.
-  const extensions::Extension* InstallExtension(
-      const base::FilePath& path,
-      int expected_change,
-      extensions::Manifest::Location install_source) {
+  const Extension* InstallExtension(const base::FilePath& path,
+                                    int expected_change,
+                                    Manifest::Location install_source) {
     return InstallOrUpdateExtension(std::string(),
                                     path,
                                     INSTALL_UI_TYPE_NONE,
@@ -174,69 +169,63 @@
   // Installs an extension and grants it the permissions it requests.
   // TODO(devlin): It seems like this is probably the desired outcome most of
   // the time - otherwise the extension installs in a disabled state.
-  const extensions::Extension* InstallExtensionWithPermissionsGranted(
+  const Extension* InstallExtensionWithPermissionsGranted(
       const base::FilePath& file_path,
       int expected_change) {
     return InstallOrUpdateExtension(
         std::string(), file_path, INSTALL_UI_TYPE_NONE, expected_change,
-        extensions::Manifest::INTERNAL, browser(),
-        extensions::Extension::NO_FLAGS, false, true);
+        Manifest::INTERNAL, browser(), Extension::NO_FLAGS, false, true);
   }
 
   // Installs bookmark app for |info|.
-  const extensions::Extension* InstallBookmarkApp(WebApplicationInfo info);
+  const Extension* InstallBookmarkApp(WebApplicationInfo info);
 
   // Installs extension as if it came from the Chrome Webstore.
-  const extensions::Extension* InstallExtensionFromWebstore(
-      const base::FilePath& path, int expected_change);
+  const Extension* InstallExtensionFromWebstore(const base::FilePath& path,
+                                                int expected_change);
 
   // Same as above but passes an id to CrxInstaller and does not allow a
   // privilege increase.
-  const extensions::Extension* UpdateExtension(const std::string& id,
-                                               const base::FilePath& path,
-                                               int expected_change) {
+  const Extension* UpdateExtension(const std::string& id,
+                                   const base::FilePath& path,
+                                   int expected_change) {
     return InstallOrUpdateExtension(id, path, INSTALL_UI_TYPE_NONE,
                                     expected_change);
   }
 
   // Same as UpdateExtension but waits for the extension to be idle first.
-  const extensions::Extension* UpdateExtensionWaitForIdle(
-      const std::string& id, const base::FilePath& path, int expected_change);
+  const Extension* UpdateExtensionWaitForIdle(const std::string& id,
+                                              const base::FilePath& path,
+                                              int expected_change);
 
   // Same as |InstallExtension| but with the normal extension UI showing up
   // (for e.g. info bar on success).
-  const extensions::Extension* InstallExtensionWithUI(
-      const base::FilePath& path,
-      int expected_change) {
+  const Extension* InstallExtensionWithUI(const base::FilePath& path,
+                                          int expected_change) {
     return InstallOrUpdateExtension(
         std::string(), path, INSTALL_UI_TYPE_NORMAL, expected_change);
   }
 
-  const extensions::Extension* InstallExtensionWithUIAutoConfirm(
-      const base::FilePath& path,
-      int expected_change,
-      Browser* browser) {
-    return InstallOrUpdateExtension(std::string(),
-                                    path,
-                                    INSTALL_UI_TYPE_AUTO_CONFIRM,
-                                    expected_change,
-                                    browser,
-                                    extensions::Extension::NO_FLAGS);
+  const Extension* InstallExtensionWithUIAutoConfirm(const base::FilePath& path,
+                                                     int expected_change,
+                                                     Browser* browser) {
+    return InstallOrUpdateExtension(
+        std::string(), path, INSTALL_UI_TYPE_AUTO_CONFIRM, expected_change,
+        browser, Extension::NO_FLAGS);
   }
 
-  const extensions::Extension* InstallExtensionWithSourceAndFlags(
+  const Extension* InstallExtensionWithSourceAndFlags(
       const base::FilePath& path,
       int expected_change,
-      extensions::Manifest::Location install_source,
-      extensions::Extension::InitFromValueFlags creation_flags) {
+      Manifest::Location install_source,
+      Extension::InitFromValueFlags creation_flags) {
     return InstallOrUpdateExtension(std::string(), path, INSTALL_UI_TYPE_NONE,
                                     expected_change, install_source, browser(),
                                     creation_flags, false, false);
   }
 
   // Begins install process but simulates a user cancel.
-  const extensions::Extension* StartInstallButCancel(
-      const base::FilePath& path) {
+  const Extension* StartInstallButCancel(const base::FilePath& path) {
     return InstallOrUpdateExtension(
         std::string(), path, INSTALL_UI_TYPE_CANCEL, 0);
   }
@@ -313,19 +302,18 @@
   // Looks for an ExtensionHost whose URL has the given path component
   // (including leading slash).  Also verifies that the expected number of hosts
   // are loaded.
-  extensions::ExtensionHost* FindHostWithPath(
-      extensions::ProcessManager* manager,
-      const std::string& path,
-      int expected_hosts);
+  ExtensionHost* FindHostWithPath(ProcessManager* manager,
+                                  const std::string& path,
+                                  int expected_hosts);
 
   // Returns
-  // extensions::browsertest_util::ExecuteScriptInBackgroundPage(profile(),
+  // browsertest_util::ExecuteScriptInBackgroundPage(profile(),
   // extension_id, script).
   std::string ExecuteScriptInBackgroundPage(const std::string& extension_id,
                                             const std::string& script);
 
   // Returns
-  // extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait(
+  // browsertest_util::ExecuteScriptInBackgroundPageNoWait(
   // profile(), extension_id, script).
   bool ExecuteScriptInBackgroundPageNoWait(const std::string& extension_id,
                                            const std::string& script);
@@ -343,8 +331,7 @@
   // TODO(michaelpg): Don't override protected data members.
   base::FilePath test_data_dir_;
 
-  std::unique_ptr<extensions::ChromeExtensionTestNotificationObserver>
-      observer_;
+  std::unique_ptr<ChromeExtensionTestNotificationObserver> observer_;
 
  private:
   // Temporary directory for testing.
@@ -359,41 +346,38 @@
     INSTALL_UI_TYPE_AUTO_CONFIRM,
   };
 
-  const extensions::Extension* InstallOrUpdateExtension(
-      const std::string& id,
-      const base::FilePath& path,
-      InstallUIType ui_type,
-      int expected_change);
-  const extensions::Extension* InstallOrUpdateExtension(
+  const Extension* InstallOrUpdateExtension(const std::string& id,
+                                            const base::FilePath& path,
+                                            InstallUIType ui_type,
+                                            int expected_change);
+  const Extension* InstallOrUpdateExtension(
       const std::string& id,
       const base::FilePath& path,
       InstallUIType ui_type,
       int expected_change,
       Browser* browser,
-      extensions::Extension::InitFromValueFlags creation_flags);
-  const extensions::Extension* InstallOrUpdateExtension(
+      Extension::InitFromValueFlags creation_flags);
+  const Extension* InstallOrUpdateExtension(const std::string& id,
+                                            const base::FilePath& path,
+                                            InstallUIType ui_type,
+                                            int expected_change,
+                                            Manifest::Location install_source);
+  const Extension* InstallOrUpdateExtension(
       const std::string& id,
       const base::FilePath& path,
       InstallUIType ui_type,
       int expected_change,
-      extensions::Manifest::Location install_source);
-  const extensions::Extension* InstallOrUpdateExtension(
-      const std::string& id,
-      const base::FilePath& path,
-      InstallUIType ui_type,
-      int expected_change,
-      extensions::Manifest::Location install_source,
+      Manifest::Location install_source,
       Browser* browser,
-      extensions::Extension::InitFromValueFlags creation_flags,
+      Extension::InitFromValueFlags creation_flags,
       bool wait_for_idle,
       bool grant_permissions);
 
   // Make the current channel "dev" for the duration of the test.
-  extensions::ScopedCurrentChannel current_channel_;
+  ScopedCurrentChannel current_channel_;
 
   // Disable external install UI.
-  extensions::FeatureSwitch::ScopedOverride
-      override_prompt_for_external_extensions_;
+  FeatureSwitch::ScopedOverride override_prompt_for_external_extensions_;
 
 #if defined(OS_WIN)
   // Use mock shortcut directories to ensure app shortcuts are cleaned up.
@@ -408,21 +392,23 @@
   Profile* profile_;
 
   // Cache cache implementation.
-  std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache_;
+  std::unique_ptr<ExtensionCacheFake> test_extension_cache_;
 
   // An override so that chrome-extensions://<extension_id>/_test_resources/foo
   // maps to chrome/test/data/extensions/foo.
-  extensions::ExtensionProtocolTestHandler test_protocol_handler_;
+  ExtensionProtocolTestHandler test_protocol_handler_;
 
   // Conditionally disable content verification.
-  std::unique_ptr<extensions::ScopedIgnoreContentVerifierForTest>
+  std::unique_ptr<ScopedIgnoreContentVerifierForTest>
       ignore_content_verification_;
 
   // Conditionally disable install verification.
-  std::unique_ptr<extensions::ScopedInstallVerifierBypassForTest>
+  std::unique_ptr<ScopedInstallVerifierBypassForTest>
       ignore_install_verification_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionBrowserTest);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_BROWSERTEST_H_
diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc
index 7e99f86..caed3e96 100644
--- a/chrome/browser/extensions/extension_context_menu_browsertest.cc
+++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc
@@ -32,7 +32,8 @@
 using extensions::MenuItem;
 using ui::MenuModel;
 
-class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest {
+class ExtensionContextMenuBrowserTest
+    : public extensions::ExtensionBrowserTest {
  public:
   // Helper to load an extension from context_menus/|subdirectory| in the
   // extensions test data dir.
diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
index 6be68a53..03070422 100644
--- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
+++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
@@ -39,10 +39,10 @@
 using extensions::Extension;
 using extensions::ExtensionRegistry;
 
-class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
+class ExtensionCrashRecoveryTest : public extensions::ExtensionBrowserTest {
  protected:
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     display_service_ =
         std::make_unique<NotificationDisplayServiceTester>(profile());
   }
@@ -107,7 +107,7 @@
   }
 
   void LoadTestExtension() {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
     const Extension* extension = LoadExtension(
         test_data_dir_.AppendASCII("common").AppendASCII("background_page"));
     ASSERT_TRUE(extension);
diff --git a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
index b12f1da..4b6422b 100644
--- a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
@@ -44,16 +44,17 @@
 using extensions::ExtensionPrefs;
 using extensions::ExtensionSyncData;
 
-class ExtensionDisabledGlobalErrorTest : public ExtensionBrowserTest {
+class ExtensionDisabledGlobalErrorTest
+    : public extensions::ExtensionBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(switches::kAppsGalleryUpdateURL,
                                     "http://localhost/autoupdate/updates.xml");
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     EXPECT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
     service_ = extensions::ExtensionSystem::Get(profile())->extension_service();
     registry_ = ExtensionRegistry::Get(profile());
diff --git a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
index 5ea929eff..71928f9f 100644
--- a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
@@ -121,8 +121,7 @@
                                            const std::string& version,
                                            const base::FilePath& path) {
     return ExtensionBuilder("test")
-        .MergeManifest(
-            DictionaryBuilder().Set(manifest_keys::kVersion, version).Build())
+        .SetManifestKey(manifest_keys::kVersion, version)
         .SetID(id)
         .SetPath(path)
         .SetLocation(Manifest::INTERNAL)
diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc
index 8902bd59..3be556c5 100644
--- a/chrome/browser/extensions/extension_install_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc
@@ -33,7 +33,7 @@
 using extensions::AppSorting;
 using extensions::Extension;
 
-class ExtensionInstallUIBrowserTest : public ExtensionBrowserTest {
+class ExtensionInstallUIBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   ExtensionInstallUIBrowserTest() {}
   ~ExtensionInstallUIBrowserTest() override {}
diff --git a/chrome/browser/extensions/extension_nacl_browsertest.cc b/chrome/browser/extensions/extension_nacl_browsertest.cc
index 37d66d1e..a719afe 100644
--- a/chrome/browser/extensions/extension_nacl_browsertest.cc
+++ b/chrome/browser/extensions/extension_nacl_browsertest.cc
@@ -38,12 +38,12 @@
 
 // This class tests that the Native Client plugin is blocked unless the
 // .nexe is part of an extension from the Chrome Webstore.
-class NaClExtensionTest : public ExtensionBrowserTest {
+class NaClExtensionTest : public extensions::ExtensionBrowserTest {
  public:
   NaClExtensionTest() {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
     ASSERT_TRUE(embedded_test_server()->Start());
   }
@@ -93,7 +93,7 @@
 
       case INSTALL_TYPE_NON_WEBSTORE:
         // Install native_client.crx but not from the webstore.
-        if (ExtensionBrowserTest::InstallExtension(file_path, 1)) {
+        if (extensions::ExtensionBrowserTest::InstallExtension(file_path, 1)) {
           extension = service->GetExtensionById(last_loaded_extension_id(),
                                                 false);
         }
diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc
index f1dddbc..47eb4a1 100644
--- a/chrome/browser/extensions/extension_protocols_unittest.cc
+++ b/chrome/browser/extensions/extension_protocols_unittest.cc
@@ -122,11 +122,9 @@
 }
 
 scoped_refptr<Extension> CreateTestResponseHeaderExtension() {
-  DictionaryBuilder web_accessible_resources;
-  web_accessible_resources.Set("web_accessible_resources",
-                               ListBuilder().Append("test.dat").Build());
   return ExtensionBuilder("An extension with web-accessible resources")
-      .MergeManifest(web_accessible_resources.Build())
+      .SetManifestKey("web_accessible_resources",
+                      ListBuilder().Append("test.dat").Build())
       .SetPath(GetTestPath("response_headers"))
       .Build();
 }
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 5c392d5e..8dc5a4a 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -7215,16 +7215,10 @@
 // Test that calls to disable Shared Modules do not work.
 TEST_F(ExtensionServiceTest, CannotDisableSharedModules) {
   InitializeEmptyExtensionService();
-  std::unique_ptr<base::DictionaryValue> export_dict =
-      extensions::DictionaryBuilder()
-          .Set("resources", extensions::ListBuilder().Append("foo.js").Build())
-          .Build();
-
   scoped_refptr<Extension> extension =
       ExtensionBuilder("Shared Module")
-          .MergeManifest(extensions::DictionaryBuilder()
-                             .Set("export", std::move(export_dict))
-                             .Build())
+          .SetManifestPath({"export", "resources"},
+                           extensions::ListBuilder().Append("foo.js").Build())
           .AddFlags(Extension::FROM_WEBSTORE)
           .Build();
 
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index b1b86b1..347c7a25 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -325,7 +325,7 @@
 
 // TODO(catmullings): Remove test in future chrome release, perhaps M59.
 class DeprecatedLoadComponentExtensionSwitchBrowserTest
-    : public ExtensionBrowserTest {
+    : public extensions::ExtensionBrowserTest {
  public:
   DeprecatedLoadComponentExtensionSwitchBrowserTest() {}
 
@@ -338,7 +338,7 @@
 
 void DeprecatedLoadComponentExtensionSwitchBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   base::FilePath fp1(test_data_dir_.AppendASCII("app_dot_com_app/"));
   base::FilePath fp2(test_data_dir_.AppendASCII("app/"));
 
@@ -371,7 +371,8 @@
   EXPECT_FALSE(is_app_test_extension_installed);
 }
 
-class DisableExtensionsExceptBrowserTest : public ExtensionBrowserTest {
+class DisableExtensionsExceptBrowserTest
+    : public extensions::ExtensionBrowserTest {
  public:
   DisableExtensionsExceptBrowserTest() {}
 
@@ -384,7 +385,7 @@
 
 void DisableExtensionsExceptBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   base::FilePath fp1(test_data_dir_.AppendASCII("app_dot_com_app/"));
   base::FilePath fp2(test_data_dir_.AppendASCII("app/"));
 
diff --git a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
index 08417d3a..67fd950d 100644
--- a/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
+++ b/chrome/browser/extensions/extension_url_rewrite_browsertest.cc
@@ -26,11 +26,11 @@
 
 using content::NavigationEntry;
 
-class ExtensionURLRewriteBrowserTest : public ExtensionBrowserTest {
+class ExtensionURLRewriteBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   void SetUp() override {
     extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
-    ExtensionBrowserTest::SetUp();
+    extensions::ExtensionBrowserTest::SetUp();
   }
 
  protected:
diff --git a/chrome/browser/extensions/extension_web_ui_unittest.cc b/chrome/browser/extensions/extension_web_ui_unittest.cc
index 51dfbcf7..0261326 100644
--- a/chrome/browser/extensions/extension_web_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_web_ui_unittest.cc
@@ -162,14 +162,9 @@
 TEST_F(ExtensionWebUITest, TestRemovingDuplicateEntriesForHosts) {
   // Test that duplicate entries for a single extension are removed. This could
   // happen because of https://crbug.com/782959.
-  std::unique_ptr<base::DictionaryValue> manifest_overrides =
-      DictionaryBuilder().Set("newtab", "newtab.html").Build();
   scoped_refptr<const Extension> extension =
       ExtensionBuilder("extension")
-          .MergeManifest(
-              DictionaryBuilder()
-                  .Set("chrome_url_overrides", std::move(manifest_overrides))
-                  .Build())
+          .SetManifestPath({"chrome_url_overrides", "newtab"}, "newtab.html")
           .Build();
 
   const GURL newtab_url = extension->GetResourceURL("newtab.html");
diff --git a/chrome/browser/extensions/file_iframe_apitest.cc b/chrome/browser/extensions/file_iframe_apitest.cc
index ad92577..1ccb708 100644
--- a/chrome/browser/extensions/file_iframe_apitest.cc
+++ b/chrome/browser/extensions/file_iframe_apitest.cc
@@ -13,7 +13,7 @@
 #include "extensions/test/test_extension_dir.h"
 #include "net/base/filename_util.h"
 
-class FileIFrameAPITest : public ExtensionBrowserTest {
+class FileIFrameAPITest : public extensions::ExtensionBrowserTest {
  public:
   FileIFrameAPITest() {}
   void set_has_all_urls(bool val) { has_all_urls_ = val; }
diff --git a/chrome/browser/extensions/gpu_browsertest.cc b/chrome/browser/extensions/gpu_browsertest.cc
index 44bc1fd9..93df6f3 100644
--- a/chrome/browser/extensions/gpu_browsertest.cc
+++ b/chrome/browser/extensions/gpu_browsertest.cc
@@ -9,6 +9,8 @@
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/process_manager.h"
 
+namespace extensions {
+
 // Tests that background pages are marked as never visible to prevent GPU
 // resource allocation. See crbug.com/362165 and crbug.com/163698.
 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, BackgroundPageIsNeverVisible) {
@@ -17,10 +19,10 @@
                     .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                     .AppendASCII("1.0.0.0")));
 
-  extensions::ProcessManager* manager =
-      extensions::ProcessManager::Get(browser()->profile());
-  extensions::ExtensionHost* host =
-      FindHostWithPath(manager, "/backgroundpage.html", 1);
+  ProcessManager* manager = ProcessManager::Get(browser()->profile());
+  ExtensionHost* host = FindHostWithPath(manager, "/backgroundpage.html", 1);
   ASSERT_TRUE(host->host_contents()->GetDelegate()->IsNeverVisible(
       host->host_contents()));
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/install_verifier_unittest.cc b/chrome/browser/extensions/install_verifier_unittest.cc
index bfe3ac5..c6cd966 100644
--- a/chrome/browser/extensions/install_verifier_unittest.cc
+++ b/chrome/browser/extensions/install_verifier_unittest.cc
@@ -97,10 +97,8 @@
     ExtensionBuilder extension_builder(test_case.test_name);
     extension_builder.SetLocation(test_case.location);
     if (test_case.update_url) {
-      extension_builder.MergeManifest(
-          DictionaryBuilder()
-              .Set("update_url", test_case.update_url->spec())
-              .Build());
+      extension_builder.SetManifestKey("update_url",
+                                       test_case.update_url->spec());
     }
     scoped_refptr<const Extension> extension = extension_builder.Build();
 
diff --git a/chrome/browser/extensions/permission_messages_unittest.cc b/chrome/browser/extensions/permission_messages_unittest.cc
index 91e2ed6..8701323 100644
--- a/chrome/browser/extensions/permission_messages_unittest.cc
+++ b/chrome/browser/extensions/permission_messages_unittest.cc
@@ -56,12 +56,9 @@
       std::unique_ptr<base::ListValue> required_permissions,
       std::unique_ptr<base::ListValue> optional_permissions) {
     app_ = ExtensionBuilder("Test")
-               .MergeManifest(
-                   DictionaryBuilder()
-                       .Set("permissions", std::move(required_permissions))
-                       .Set("optional_permissions",
-                            std::move(optional_permissions))
-                       .Build())
+               .SetManifestKey("permissions", std::move(required_permissions))
+               .SetManifestKey("optional_permissions",
+                               std::move(optional_permissions))
                .SetID(crx_file::id_util::GenerateId("extension"))
                .SetLocation(Manifest::INTERNAL)
                .Build();
diff --git a/chrome/browser/extensions/subscribe_page_action_browsertest.cc b/chrome/browser/extensions/subscribe_page_action_browsertest.cc
index c257c5c..11ff43fe 100644
--- a/chrome/browser/extensions/subscribe_page_action_browsertest.cc
+++ b/chrome/browser/extensions/subscribe_page_action_browsertest.cc
@@ -14,7 +14,8 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 using content::WebContents;
-using extensions::Extension;
+
+namespace extensions {
 
 namespace {
 
@@ -68,10 +69,9 @@
   if (direct_url) {
     // We navigate directly to the subscribe page for feeds where the feed
     // sniffing won't work, in other words, as is the case for malformed feeds.
-    return GURL(std::string(extensions::kExtensionScheme) +
-        url::kStandardSchemeSeparator +
-        extension_id + std::string(kSubscribePage) + std::string("?") +
-        feed_url.spec() + std::string("&synchronous"));
+    return GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator +
+                extension_id + std::string(kSubscribePage) + std::string("?") +
+                feed_url.spec() + std::string("&synchronous"));
   } else {
     // Navigate to the feed content (which will cause the extension to try to
     // sniff the type and display the subscribe page in another tab.
@@ -328,3 +328,5 @@
                             browser(), id, true, "Feed for MyFeedTitle",
                             "Title with no link", "Desc", "No error");
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/view_extension_source_browsertest.cc b/chrome/browser/extensions/view_extension_source_browsertest.cc
index 4dbf31f..bb323ff3 100644
--- a/chrome/browser/extensions/view_extension_source_browsertest.cc
+++ b/chrome/browser/extensions/view_extension_source_browsertest.cc
@@ -19,7 +19,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
-typedef ExtensionBrowserTest ViewExtensionSourceTest;
+typedef extensions::ExtensionBrowserTest ViewExtensionSourceTest;
 
 // Verify that restoring a view-source tab for a Chrome extension works
 // properly.  See https://crbug.com/699428.
diff --git a/chrome/browser/extensions/wasm_app_browsertest.cc b/chrome/browser/extensions/wasm_app_browsertest.cc
index 67f0ee5..f235922 100644
--- a/chrome/browser/extensions/wasm_app_browsertest.cc
+++ b/chrome/browser/extensions/wasm_app_browsertest.cc
@@ -9,7 +9,7 @@
 // Test ensures that Wasm can run in Chrome Apps.
 namespace {
 
-class WasmAppTest : public ExtensionBrowserTest {};
+using WasmAppTest = extensions::ExtensionBrowserTest;
 
 IN_PROC_BROWSER_TEST_F(WasmAppTest, InstantiateWasmFromFetch) {
   const Extension* extension =
diff --git a/chrome/browser/extensions/web_contents_browsertest.cc b/chrome/browser/extensions/web_contents_browsertest.cc
index 349d88bb..78cce467 100644
--- a/chrome/browser/extensions/web_contents_browsertest.cc
+++ b/chrome/browser/extensions/web_contents_browsertest.cc
@@ -22,10 +22,9 @@
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
 
+namespace extensions {
 namespace {
 
-using ExtensionApiFrameIdMap = extensions::ExtensionApiFrameIdMap;
-
 content::WebContents* GetActiveWebContents(const Browser* browser) {
   return browser->tab_strip_model()->GetActiveWebContents();
 }
@@ -37,7 +36,7 @@
   explicit ExtensionNavigationUIDataObserver(content::WebContents* web_contents)
       : WebContentsObserver(web_contents) {}
 
-  const extensions::ExtensionNavigationUIData* GetExtensionNavigationUIData(
+  const ExtensionNavigationUIData* GetExtensionNavigationUIData(
       content::RenderFrameHost* rfh) const {
     auto iter = navigation_ui_data_map_.find(rfh);
     if (iter == navigation_ui_data_map_.end())
@@ -59,7 +58,7 @@
   }
 
   std::map<content::RenderFrameHost*,
-           std::unique_ptr<extensions::ExtensionNavigationUIData>>
+           std::unique_ptr<ExtensionNavigationUIData>>
       navigation_ui_data_map_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionNavigationUIDataObserver);
@@ -102,7 +101,7 @@
   auto test_devtools_main_frame_cached = [](Browser* browser, bool is_docked) {
     SCOPED_TRACE(base::StringPrintf("Testing a %s devtools window",
                                     is_docked ? "docked" : "undocked"));
-    const auto* api_frame_id_map = extensions::ExtensionApiFrameIdMap::Get();
+    const auto* api_frame_id_map = ExtensionApiFrameIdMap::Get();
     size_t prior_count = api_frame_id_map->GetFrameDataCountForTesting();
 
     // Open a devtools window.
@@ -133,7 +132,7 @@
 // Regression test for crbug.com/810614.
 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, FrameDataCached) {
   // Load an extension with a web accessible resource.
-  const extensions::Extension* extension =
+  const Extension* extension =
       LoadExtension(test_data_dir_.AppendASCII("web_accessible_resources"));
   ASSERT_TRUE(extension);
 
@@ -306,3 +305,5 @@
     EXPECT_FALSE(frame_data.pending_main_frame_url);
   }
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/webstore_installer_test.cc b/chrome/browser/extensions/webstore_installer_test.cc
index 4d7452c..7bdca8c9 100644
--- a/chrome/browser/extensions/webstore_installer_test.cc
+++ b/chrome/browser/extensions/webstore_installer_test.cc
@@ -55,7 +55,7 @@
 WebstoreInstallerTest::~WebstoreInstallerTest() {}
 
 void WebstoreInstallerTest::SetUpCommandLine(base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 
   embedded_test_server()->RegisterRequestMonitor(base::Bind(
       &WebstoreInstallerTest::ProcessServerRequest, base::Unretained(this)));
@@ -80,7 +80,7 @@
 }
 
 void WebstoreInstallerTest::SetUpOnMainThread() {
-  ExtensionBrowserTest::SetUpOnMainThread();
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
   host_resolver()->AddRule(webstore_domain_, "127.0.0.1");
   host_resolver()->AddRule(verified_domain_, "127.0.0.1");
diff --git a/chrome/browser/extensions/webstore_installer_test.h b/chrome/browser/extensions/webstore_installer_test.h
index d1d9fb6..ccb8514 100644
--- a/chrome/browser/extensions/webstore_installer_test.h
+++ b/chrome/browser/extensions/webstore_installer_test.h
@@ -27,7 +27,7 @@
 }
 }
 
-class WebstoreInstallerTest : public ExtensionBrowserTest {
+class WebstoreInstallerTest : public extensions::ExtensionBrowserTest {
  public:
   WebstoreInstallerTest(const std::string& webstore_domain,
                         const std::string& test_data_path,
diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc
index f6f9adef..ab8b96f 100644
--- a/chrome/browser/extensions/window_open_apitest.cc
+++ b/chrome/browser/extensions/window_open_apitest.cc
@@ -57,6 +57,8 @@
 class Window;
 }
 
+namespace extensions {
+
 class WindowOpenApiTest : public ExtensionApiTest {
   void SetUpOnMainThread() override {
     ExtensionApiTest::SetUpOnMainThread();
@@ -524,3 +526,5 @@
             extension->install_warnings().front().message);
 }
 #endif
+
+}  // namespace extensions
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 9e62e3a..4ef8aae9 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -405,7 +405,8 @@
 
 const char kEnableEmojiContextMenuName[] = "Emoji Context Menu";
 const char kEnableEmojiContextMenuDescription[] =
-    "Enables the Emoji picker item in context menus for editable text areas.";
+    "Enables the Emoji picker item in context menus for editable text areas, if"
+    " supported by the operating system.";
 
 const char kEnableEnumeratingAudioDevicesName[] =
     "Experimentally enable enumerating audio devices.";
@@ -1193,6 +1194,11 @@
 const char kOverscrollStartThreshold166Percent[] = "166%";
 const char kOverscrollStartThreshold200Percent[] = "200%";
 
+const char kTouchpadOverscrollHistoryNavigationName[] =
+    "Overscroll history navigation on Touchpad";
+const char kTouchpadOverscrollHistoryNavigationDescription[] =
+    "Allows swipe left/right from touchpad change browser navigation.";
+
 const char kParallelDownloadingName[] = "Parallel downloading";
 const char kParallelDownloadingDescription[] =
     "Enable parallel downloading to accelerate download speed.";
@@ -1286,12 +1292,6 @@
     "Pull-to-refresh gesture in response to vertical overscroll.";
 const char kPullToRefreshEnabledTouchscreen[] = "Enabled for touchscreen only";
 
-const char kPushApiBackgroundModeName[] = "Enable Push API background mode";
-const char kPushApiBackgroundModeDescription[] =
-    "Enable background mode for the Push API. This allows Chrome to continue "
-    "running after the last window is closed, and to launch at OS startup, if "
-    "the Push API needs it.";
-
 const char kQueryInOmniboxName[] = "Query in Omnibox";
 const char kQueryInOmniboxDescription[] =
     "Only display query terms in the omnibox when viewing a search results "
@@ -1938,13 +1938,6 @@
     "Expand bottom sheet on startup in Chrome Home after a period of"
     " inactivity.";
 
-const char kChromeHomeMenuItemsName[] =
-    "Show bookmarks, downloads, and history menu items in Chrome Home";
-const char kChromeHomeMenuItemsDescription[] =
-    "Shows bookmarks, downloads, and history menu items in the overflow menu"
-    " when Chrome Home is enabled. These items will open the bottom sheet to"
-    " show the desired content.";
-
 const char kChromeHomePersistentIphName[] = "Chrome Home Persistent Iph";
 const char kChromeHomePersistentIphDescription[] =
     "Wait to dismiss the Chrome Home IPH until the user inteacts with the "
@@ -2976,11 +2969,6 @@
     "Enables inspection of native UI elements. For local inspection use "
     "chrome://inspect#other";
 
-const char kUseMusName[] = "Mojo UI service (mus)";
-const char kUseMusDescription[] =
-    "Handles input events, display configuration, and windowing in a mojo "
-    "service on a thread in the browser process.";
-
 const char kUseMashName[] = "Out-of-process system UI (mash)";
 const char kUseMashDescription[] =
     "Runs the mojo UI service (mus) and the ash window manager and system UI "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 56ec86f..f6d0f353 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -806,9 +806,6 @@
 extern const char kPullToRefreshDescription[];
 extern const char kPullToRefreshEnabledTouchscreen[];
 
-extern const char kPushApiBackgroundModeName[];
-extern const char kPushApiBackgroundModeDescription[];
-
 extern const char kQueryInOmniboxName[];
 extern const char kQueryInOmniboxDescription[];
 
@@ -1023,6 +1020,9 @@
 extern const char kTouchEventsName[];
 extern const char kTouchEventsDescription[];
 
+extern const char kTouchpadOverscrollHistoryNavigationName[];
+extern const char kTouchpadOverscrollHistoryNavigationDescription[];
+
 extern const char kTouchSelectionStrategyName[];
 extern const char kTouchSelectionStrategyDescription[];
 extern const char kTouchSelectionStrategyCharacter[];
@@ -1184,9 +1184,6 @@
 extern const char kChromeHomeInactivitySheetExpansionName[];
 extern const char kChromeHomeInactivitySheetExpansionDescription[];
 
-extern const char kChromeHomeMenuItemsName[];
-extern const char kChromeHomeMenuItemsDescription[];
-
 extern const char kChromeHomePersistentIphName[];
 extern const char kChromeHomePersistentIphDescription[];
 
@@ -1815,9 +1812,6 @@
 extern const char kUiDevToolsName[];
 extern const char kUiDevToolsDescription[];
 
-extern const char kUseMusName[];
-extern const char kUseMusDescription[];
-
 extern const char kUseMashName[];
 extern const char kUseMashDescription[];
 
diff --git a/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm b/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
index 57a68399..43384244 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac_browsertest.mm
@@ -17,7 +17,7 @@
 
 using cocoa_test_event_utils::SynthesizeKeyEvent;
 
-using GlobalKeyboardShortcutsTest = ExtensionBrowserTest;
+using GlobalKeyboardShortcutsTest = extensions::ExtensionBrowserTest;
 
 namespace {
 
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 9e1f6fb..3fe13ea 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -37,15 +37,10 @@
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/net/dns_probe_service.h"
 #include "chrome/browser/net/proxy_service_factory.h"
-#include "chrome/browser/net/sth_distributor_provider.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/sth_distributor.h"
-#include "components/certificate_transparency/sth_observer.h"
-#include "components/certificate_transparency/tree_state_tracker.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_usage/core/data_use_aggregator.h"
 #include "components/data_usage/core/data_use_amortizer.h"
@@ -72,8 +67,6 @@
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/ct_known_logs.h"
 #include "net/cert/ct_log_verifier.h"
-#include "net/cert/ct_verifier.h"
-#include "net/cert/multi_log_ct_verifier.h"
 #include "net/cert/multi_threaded_cert_verifier.h"
 #include "net/dns/host_cache.h"
 #include "net/dns/host_resolver.h"
@@ -419,9 +412,6 @@
   ntlm_v2_enabled_.MoveToThread(io_thread_proxy);
 #endif
 
-  chrome_browser_net::SetGlobalSTHDistributor(
-      std::make_unique<certificate_transparency::STHDistributor>());
-
   BrowserThread::SetIOThreadDelegate(this);
 
   system_network_context_manager->SetUp(&network_context_request_,
@@ -435,10 +425,6 @@
   BrowserThread::SetIOThreadDelegate(nullptr);
 
   DCHECK(!globals_);
-
-  // Destroy the old distributor to check that the observers list it holds is
-  // empty.
-  chrome_browser_net::SetGlobalSTHDistributor(nullptr);
 }
 
 IOThread::Globals* IOThread::globals() {
@@ -544,17 +530,6 @@
 
   UpdateDnsClientEnabled();
 
-  if (base::FeatureList::IsEnabled(certificate_transparency::kCTLogAuditing)) {
-    ct_tree_tracker_ =
-        std::make_unique<certificate_transparency::TreeStateTracker>(
-            globals_->ct_logs,
-            globals_->system_request_context->host_resolver(), net_log_);
-    // Register the ct_tree_tracker_ as observer for new STHs.
-    RegisterSTHObserver(ct_tree_tracker_.get());
-    // Register the ct_tree_tracker_ as observer for verified SCTs.
-    globals_->system_request_context->cert_transparency_verifier()->SetObserver(
-        ct_tree_tracker_.get());
-  }
 }
 
 void IOThread::CleanUp() {
@@ -562,17 +537,6 @@
 
   system_url_request_context_getter_ = nullptr;
 
-  if (ct_tree_tracker_) {
-    // Unlink the ct_tree_tracker_ from the global cert_transparency_verifier
-    // and unregister it from new STH notifications so it will take no actions
-    // on anything observed during CleanUp process.
-    globals()
-        ->system_request_context->cert_transparency_verifier()
-        ->SetObserver(nullptr);
-    UnregisterSTHObserver(ct_tree_tracker_.get());
-    ct_tree_tracker_.reset();
-  }
-
   globals_->system_request_context->proxy_resolution_service()->OnShutdown();
 
 #if defined(USE_NSS_CERTS)
@@ -734,16 +698,6 @@
   }
 }
 
-void IOThread::RegisterSTHObserver(
-    certificate_transparency::STHObserver* observer) {
-  chrome_browser_net::GetGlobalSTHDistributor()->RegisterObserver(observer);
-}
-
-void IOThread::UnregisterSTHObserver(
-    certificate_transparency::STHObserver* observer) {
-  chrome_browser_net::GetGlobalSTHDistributor()->UnregisterObserver(observer);
-}
-
 void IOThread::SetUpProxyService(
     network::URLRequestContextBuilderMojo* builder) const {
 #if defined(OS_CHROMEOS)
@@ -752,10 +706,6 @@
 #endif
 }
 
-certificate_transparency::TreeStateTracker* IOThread::ct_tree_tracker() const {
-  return ct_tree_tracker_.get();
-}
-
 void IOThread::ConstructSystemRequestContext() {
   std::unique_ptr<network::URLRequestContextBuilderMojo> builder =
       std::make_unique<network::URLRequestContextBuilderMojo>();
@@ -805,12 +755,6 @@
       command_line.HasSwitch(
           network::switches::kIgnoreCertificateErrorsSPKIList));
 
-  std::unique_ptr<net::MultiLogCTVerifier> ct_verifier =
-      std::make_unique<net::MultiLogCTVerifier>();
-  // Add built-in logs
-  ct_verifier->AddLogs(globals_->ct_logs);
-  builder->set_ct_verifier(std::move(ct_verifier));
-
   SetUpProxyService(builder.get());
 
   if (!is_quic_allowed_on_init_)
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index b4c7a49..f4dec02 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -258,8 +258,6 @@
 
   Globals* globals_;
 
-  std::unique_ptr<certificate_transparency::TreeStateTracker> ct_tree_tracker_;
-
   BooleanPrefMember system_enable_referrers_;
 
   BooleanPrefMember dns_client_enabled_;
diff --git a/chrome/browser/io_thread_browsertest.cc b/chrome/browser/io_thread_browsertest.cc
index a9371b1..1785b01 100644
--- a/chrome/browser/io_thread_browsertest.cc
+++ b/chrome/browser/io_thread_browsertest.cc
@@ -113,21 +113,6 @@
                           ->GetEffectiveConnectionType());
 }
 
-void CheckSCTsAreSentToTreeTracker(IOThread* io_thread) {
-  EXPECT_NE(io_thread->ct_tree_tracker(), nullptr);
-  EXPECT_EQ(io_thread->ct_tree_tracker(),
-            io_thread->globals()
-                ->system_request_context->cert_transparency_verifier()
-                ->GetObserver());
-}
-
-void CheckSCTsAreNotSentToTreeTracker(IOThread* io_thread) {
-  EXPECT_EQ(io_thread->globals()
-                ->system_request_context->cert_transparency_verifier()
-                ->GetObserver(),
-            nullptr);
-}
-
 class IOThreadBrowserTest : public InProcessBrowserTest {
  public:
   IOThreadBrowserTest() {}
@@ -212,48 +197,6 @@
                  base::Unretained(g_browser_process->io_thread()), true, url));
 }
 
-class IOThreadCTBrowserTest : public IOThreadBrowserTest {
- public:
-  IOThreadCTBrowserTest() {}
-  ~IOThreadCTBrowserTest() override {}
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        certificate_transparency::kCTLogAuditing);
-    IOThreadBrowserTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(IOThreadCTBrowserTest, SCTsAreSentToTreeTracker) {
-  RunOnIOThreadBlocking(
-      base::BindOnce(&CheckSCTsAreSentToTreeTracker,
-                     base::Unretained(g_browser_process->io_thread())));
-}
-
-class IOThreadNoCTBrowserTest : public IOThreadBrowserTest {
- public:
-  IOThreadNoCTBrowserTest() {}
-  ~IOThreadNoCTBrowserTest() override {}
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndDisableFeature(
-        certificate_transparency::kCTLogAuditing);
-    IOThreadBrowserTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(IOThreadNoCTBrowserTest, SCTsAreNotSentToTreeTracker) {
-  RunOnIOThreadBlocking(
-      base::BindOnce(&CheckSCTsAreNotSentToTreeTracker,
-                     base::Unretained(g_browser_process->io_thread())));
-}
-
 class IOThreadEctCommandLineBrowserTest : public IOThreadBrowserTest {
  public:
   IOThreadEctCommandLineBrowserTest() {}
diff --git a/chrome/browser/media/router/issue_manager_unittest.cc b/chrome/browser/media/router/issue_manager_unittest.cc
index a0a63f4..50243e5 100644
--- a/chrome/browser/media/router/issue_manager_unittest.cc
+++ b/chrome/browser/media/router/issue_manager_unittest.cc
@@ -137,13 +137,12 @@
   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
 
   EXPECT_CALL(observer, OnIssuesCleared()).Times(1);
+  EXPECT_TRUE(task_runner_->HasPendingTask());
   manager_.ClearIssue(issue1.id());
 
   EXPECT_CALL(observer, OnIssuesCleared()).Times(0);
   base::TimeDelta timeout = IssueManager::GetAutoDismissTimeout(issue_info1);
   EXPECT_FALSE(timeout.is_zero());
-  EXPECT_TRUE(task_runner_->HasPendingTask());
-  task_runner_->FastForwardBy(timeout);
   EXPECT_FALSE(task_runner_->HasPendingTask());
 }
 
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index 015baacd..897acc2 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -198,7 +198,8 @@
 
 }  // namespace
 
-class ProcessMemoryMetricsEmitterTest : public ExtensionBrowserTest {
+class ProcessMemoryMetricsEmitterTest
+    : public extensions::ExtensionBrowserTest {
  public:
   ProcessMemoryMetricsEmitterTest() {
     scoped_feature_list_.InitAndEnableFeature(ukm::kUkmFeature);
@@ -207,7 +208,7 @@
   ~ProcessMemoryMetricsEmitterTest() override {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
   }
 
diff --git a/chrome/browser/net/default_network_context_params.cc b/chrome/browser/net/default_network_context_params.cc
index 332c063..1e3ecbd 100644
--- a/chrome/browser/net/default_network_context_params.cc
+++ b/chrome/browser/net/default_network_context_params.cc
@@ -10,6 +10,7 @@
 #include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
 #include "chrome/common/channel_info.h"
@@ -25,6 +26,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/user_agent.h"
+#include "net/cert/ct_known_logs.h"
 #include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
 
 network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams() {
@@ -68,6 +70,19 @@
   network_context_params->dangerously_allow_pac_access_to_secure_urls =
       !local_state->GetBoolean(prefs::kPacHttpsUrlStrippingEnabled);
 
+#if !defined(OS_ANDROID)
+  // CT is only enabled on Desktop platforms for now.
+  network_context_params->enforce_chrome_ct_policy = true;
+  for (const auto& ct_log : net::ct::GetKnownLogs()) {
+    // TODO(rsleevi): https://crbug.com/702062 - Remove this duplication.
+    network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
+    log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
+    log_info->name = ct_log.log_name;
+    log_info->dns_api_endpoint = ct_log.log_dns_domain;
+    network_context_params->ct_logs.push_back(std::move(log_info));
+  }
+#endif
+
   bool http_09_on_non_default_ports_enabled = false;
   const base::Value* value =
       g_browser_process->policy_service()
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index c7a37262..1d281b0 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_switches.h"
@@ -80,6 +81,7 @@
 
 enum class NetworkContextType {
   kSystem,
+  kSafeBrowsing,
   kProfile,
   kIncognitoProfile,
 };
@@ -171,6 +173,10 @@
       case NetworkContextType::kSystem:
         return g_browser_process->system_network_context_manager()
             ->GetURLLoaderFactory();
+      case NetworkContextType::kSafeBrowsing:
+        return g_browser_process->safe_browsing_service()
+            ->GetURLLoaderFactory()
+            .get();
       case NetworkContextType::kProfile:
         return content::BrowserContext::GetDefaultStoragePartition(
                    browser()->profile())
@@ -192,6 +198,8 @@
       case NetworkContextType::kSystem:
         return g_browser_process->system_network_context_manager()
             ->GetContext();
+      case NetworkContextType::kSafeBrowsing:
+        return g_browser_process->safe_browsing_service()->GetNetworkContext();
       case NetworkContextType::kProfile:
         return content::BrowserContext::GetDefaultStoragePartition(
                    browser()->profile())
@@ -209,6 +217,7 @@
   StorageType GetHttpCacheType() const {
     switch (GetParam().network_context_type) {
       case NetworkContextType::kSystem:
+      case NetworkContextType::kSafeBrowsing:
         return StorageType::kNone;
       case NetworkContextType::kProfile:
         return StorageType::kDisk;
@@ -226,6 +235,7 @@
     PrefService* pref_service = nullptr;
     switch (GetParam().network_context_type) {
       case NetworkContextType::kSystem:
+      case NetworkContextType::kSafeBrowsing:
         pref_service = g_browser_process->local_state();
         break;
       case NetworkContextType::kProfile:
@@ -247,6 +257,7 @@
     // requests are sent on a separate pipe from ProxyConfigs.
     switch (GetParam().network_context_type) {
       case NetworkContextType::kSystem:
+      case NetworkContextType::kSafeBrowsing:
         g_browser_process->system_network_context_manager()
             ->FlushProxyConfigMonitorForTesting();
         break;
@@ -334,6 +345,10 @@
         g_browser_process->system_network_context_manager()
             ->FlushNetworkInterfaceForTesting();
         break;
+      case NetworkContextType::kSafeBrowsing:
+        g_browser_process->safe_browsing_service()
+            ->FlushNetworkInterfaceForTesting();
+        break;
       case NetworkContextType::kProfile:
         content::BrowserContext::GetDefaultStoragePartition(
             browser()->profile())
@@ -604,10 +619,12 @@
 
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                        UserAgentAndLanguagePrefs) {
-  // System network context isn't associated with any profile, so changing the
-  // language settings in the default one doesn't affect what it sends.
+  // The system and SafeBrowsing network contexts aren't associated with any
+  // profile, so changing the language settings for the profile's main network
+  // context won't affect what they send.
   bool system =
-      (GetParam().network_context_type == NetworkContextType::kSystem);
+      (GetParam().network_context_type == NetworkContextType::kSystem ||
+       GetParam().network_context_type == NetworkContextType::kSafeBrowsing);
   const char kDefaultAcceptLanguage[] = "en-us,en";
 
   std::string accept_language, user_agent;
@@ -964,6 +981,11 @@
                                   NetworkContextType::kSystem})));         \
                                                                            \
   INSTANTIATE_TEST_CASE_P(                                                 \
+      SafeBrowsingNetworkContext, TestFixture,                             \
+      ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
+                                  NetworkContextType::kSafeBrowsing})));   \
+                                                                           \
+  INSTANTIATE_TEST_CASE_P(                                                 \
       ProfileMainNetworkContext, TestFixture,                              \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kProfile}),          \
@@ -981,6 +1003,9 @@
 // Instiates tests with a prefix indicating which NetworkContext is being
 // tested, and a suffix of "/0" if the network service is disabled, "/1" if it's
 // enabled, and "/2" if it's enabled and restarted.
+//
+// TODO(mmenke): Enabled tests for the SafeBrowsing NetworkContext, once it
+// works with the network service enabled.
 #define INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(TestFixture)               \
   INSTANTIATE_TEST_CASE_P(                                                 \
       SystemNetworkContext, TestFixture,                                   \
@@ -992,6 +1017,11 @@
                                   NetworkContextType::kSystem})));         \
                                                                            \
   INSTANTIATE_TEST_CASE_P(                                                 \
+      SafeBrowsingNetworkContext, TestFixture,                             \
+      ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
+                                  NetworkContextType::kSafeBrowsing})));   \
+                                                                           \
+  INSTANTIATE_TEST_CASE_P(                                                 \
       ProfileMainNetworkContext, TestFixture,                              \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kProfile}),          \
diff --git a/chrome/browser/net/sth_distributor_provider.cc b/chrome/browser/net/sth_distributor_provider.cc
deleted file mode 100644
index a0ecb31b..0000000
--- a/chrome/browser/net/sth_distributor_provider.cc
+++ /dev/null
@@ -1,27 +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 "chrome/browser/net/sth_distributor_provider.h"
-
-#include "base/lazy_instance.h"
-#include "components/certificate_transparency/sth_distributor.h"
-
-namespace chrome_browser_net {
-
-namespace {
-base::LazyInstance<std::unique_ptr<certificate_transparency::STHDistributor>>::
-    DestructorAtExit global_sth_distributor = LAZY_INSTANCE_INITIALIZER;
-}  // namespace
-
-void SetGlobalSTHDistributor(
-    std::unique_ptr<certificate_transparency::STHDistributor> distributor) {
-  global_sth_distributor.Get().swap(distributor);
-}
-
-certificate_transparency::STHDistributor* GetGlobalSTHDistributor() {
-  CHECK(global_sth_distributor.Get());
-  return global_sth_distributor.Get().get();
-}
-
-}  // namespace chrome_browser_net
diff --git a/chrome/browser/net/sth_distributor_provider.h b/chrome/browser/net/sth_distributor_provider.h
deleted file mode 100644
index c79f1ba..0000000
--- a/chrome/browser/net/sth_distributor_provider.h
+++ /dev/null
@@ -1,22 +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_BROWSER_NET_STH_DISTRIBUTOR_PROVIDER_H_
-#define CHROME_BROWSER_NET_STH_DISTRIBUTOR_PROVIDER_H_
-
-#include <memory>
-
-namespace certificate_transparency {
-class STHDistributor;
-}  // namespace certificate_transparency
-
-namespace chrome_browser_net {
-
-void SetGlobalSTHDistributor(
-    std::unique_ptr<certificate_transparency::STHDistributor> distributor);
-certificate_transparency::STHDistributor* GetGlobalSTHDistributor();
-
-}  // namespace chrome_browser_net
-
-#endif  // CHROME_BROWSER_NET_STH_DISTRIBUTOR_PROVIDER_H_
diff --git a/chrome/browser/notifications/message_center_notification_manager.h b/chrome/browser/notifications/message_center_notification_manager.h
index b051b2df..3538ddf2 100644
--- a/chrome/browser/notifications/message_center_notification_manager.h
+++ b/chrome/browser/notifications/message_center_notification_manager.h
@@ -28,7 +28,7 @@
 namespace message_center {
 class Notification;
 class NotificationBlocker;
-FORWARD_DECLARE_TEST(WebNotificationTrayTest, ManuallyCloseMessageCenter);
+FORWARD_DECLARE_TEST(NotificationTrayTest, ManuallyCloseMessageCenter);
 }
 
 #if !defined(OS_CHROMEOS)
@@ -74,7 +74,7 @@
       const std::string& delegate_id, Profile* profile);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(message_center::WebNotificationTrayTest,
+  FRIEND_TEST_ALL_PREFIXES(message_center::NotificationTrayTest,
                            ManuallyCloseMessageCenter);
 
   std::unique_ptr<message_center::UiDelegate> tray_;
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index f9e73f78..6254140 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -21,6 +21,7 @@
 #include "base/strings/nullable_string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service_impl.h"
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
index baf87d9..8253dc0c 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -28,6 +28,7 @@
           WebFeature::kPaymentHandler,
           WebFeature::kPaymentRequestShowWithoutGesture,
           WebFeature::kHTMLImports, WebFeature::kHTMLImportsHasStyleSheets,
+          WebFeature::kElementCreateShadowRoot,
       }));
   return opt_in_features.count(feature);
 }
diff --git a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
index 6917cf6..122bf70 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
@@ -8,6 +8,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
 
 using WebFeature = blink::mojom::WebFeature;
 using Features = page_load_metrics::mojom::PageLoadFeatures;
@@ -53,6 +54,14 @@
     UMA_HISTOGRAM_ENUMERATION(internal::kFeaturesHistogramName, feature,
                               WebFeature::kNumberOfFeatures);
     features_recorded_.set(static_cast<size_t>(feature));
+    // TODO(kochi): https://crbug.com/806671 as ElementCreateShadowRoot is
+    // ~12% as of April, 2018, and to meet the UKM's data volume expectation,
+    // reduce the data size by sampling. Revisit and remove this code once
+    // Shadow DOM V0 is removed.
+    const int kSamplingFactor = 10;
+    if (feature == WebFeature::kElementCreateShadowRoot &&
+        base::RandGenerator(kSamplingFactor) != 0)
+      continue;
     if (IsAllowedUkmFeature(feature)) {
       ukm::builders::Blink_UseCounter(extra_info.source_id)
           .SetFeature(static_cast<int64_t>(feature))
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 488c8ce..0bf1325 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -92,13 +92,13 @@
 
 }  // namespace
 
-class ComponentCloudPolicyTest : public ExtensionBrowserTest {
+class ComponentCloudPolicyTest : public extensions::ExtensionBrowserTest {
  protected:
   ComponentCloudPolicyTest() {}
   ~ComponentCloudPolicyTest() override {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 #if defined(OS_CHROMEOS)
     // ExtensionBrowserTest sets the login users to a non-managed value;
     // replace it. This is the default username sent in policy blobs from the
@@ -122,11 +122,11 @@
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
     command_line->AppendSwitchASCII(switches::kDeviceManagementUrl, url);
 
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     ASSERT_TRUE(PolicyServiceIsEmpty(g_browser_process->policy_service()))
         << "Pre-existing policies in this machine will make this test fail.";
 
@@ -147,7 +147,7 @@
 
   void TearDownOnMainThread() override {
     event_listener_.reset();
-    ExtensionBrowserTest::TearDownOnMainThread();
+    extensions::ExtensionBrowserTest::TearDownOnMainThread();
   }
 
   scoped_refptr<const extensions::Extension> LoadExtension(
@@ -158,7 +158,8 @@
       return NULL;
     }
     scoped_refptr<const extensions::Extension> extension(
-        ExtensionBrowserTest::LoadExtension(full_path.Append(path)));
+        extensions::ExtensionBrowserTest::LoadExtension(
+            full_path.Append(path)));
     if (!extension.get()) {
       ADD_FAILURE();
       return NULL;
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index 7163747..2063e82d 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -182,7 +182,7 @@
 // Based on top of ExtensionBrowserTest to allow easy interaction with the
 // ExtensionService.
 class PrefHashBrowserTestBase
-    : public ExtensionBrowserTest,
+    : public extensions::ExtensionBrowserTest,
       public testing::WithParamInterface<std::string> {
  public:
   // List of potential protection levels for this test in strict increasing
@@ -203,7 +203,7 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     EXPECT_FALSE(command_line->HasSwitch(switches::kForceFieldTrials));
     command_line->AppendSwitchASCII(
         switches::kForceFieldTrials,
@@ -219,7 +219,7 @@
     // Do the normal setup in the PRE test and attack preferences in the main
     // test.
     if (content::IsPreTest())
-      return ExtensionBrowserTest::SetUpUserDataDirectory();
+      return extensions::ExtensionBrowserTest::SetUpUserDataDirectory();
 
 #if defined(OS_CHROMEOS)
     // For some reason, the Preferences file does not exist in the location
@@ -284,7 +284,7 @@
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
 
     // Bots are on a domain, turn off the domain check for settings hardening in
     // order to be able to test all SettingsEnforcement groups.
@@ -324,14 +324,14 @@
       }
     }
 #endif
-    ExtensionBrowserTest::TearDown();
+    extensions::ExtensionBrowserTest::TearDown();
   }
 
   // In the PRE_ test, find the number of tracked preferences that were
   // initialized and save it to a file to be read back in the main test and used
   // as the total number of tracked preferences.
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
     // File in which the PRE_ test will save the number of tracked preferences
     // on this platform.
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc
index 970f22f..d3ba95a0 100644
--- a/chrome/browser/printing/print_browsertest.cc
+++ b/chrome/browser/printing/print_browsertest.cc
@@ -243,7 +243,7 @@
 
 constexpr char IsolateOriginsPrintBrowserTest::kIsolatedSite[];
 
-class PrintExtensionBrowserTest : public ExtensionBrowserTest {
+class PrintExtensionBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   PrintExtensionBrowserTest() {}
   ~PrintExtensionBrowserTest() override {}
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index e5da067..c064617 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -58,7 +58,6 @@
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
-#include "net/http/http_server_properties.h"
 #include "net/http/transport_security_state.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/preferences/public/cpp/in_process_service_factory.h"
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 6c1d26a6..1c483e18 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -66,8 +66,6 @@
 #include "net/cookies/cookie_store.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_session.h"
-#include "net/http/http_server_properties.h"
-#include "net/http/http_server_properties_manager.h"
 #include "net/net_buildflags.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/url_request/url_request_context_builder.h"
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index ee51e6f1..bd9dd3e 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -713,12 +713,6 @@
     domain_reliability_monitor_unowned_->Shutdown();
 
   if (main_request_context_) {
-    // Prevent the TreeStateTracker from getting any more notifications by
-    // severing the link between it and the CTVerifier and unregistering it from
-    // new STH notifications.
-    main_request_context_->cert_transparency_verifier()->SetObserver(nullptr);
-    ct_tree_tracker_unregistration_.Run();
-
     // Destroy certificate_report_sender_ before main_request_context_,
     // since the former has a reference to the latter.
     main_request_context_->transport_security_state()->SetReportSender(nullptr);
@@ -1180,23 +1174,6 @@
         std::move(profile_params_->new_tab_page_interceptor));
   }
 
-  std::unique_ptr<net::MultiLogCTVerifier> ct_verifier(
-      new net::MultiLogCTVerifier());
-  ct_verifier->AddLogs(io_thread_globals->ct_logs);
-
-  ct_tree_tracker_.reset(new certificate_transparency::TreeStateTracker(
-      io_thread_globals->ct_logs,
-      io_thread_globals->system_request_context->host_resolver(),
-      io_thread->net_log()));
-  ct_verifier->SetObserver(ct_tree_tracker_.get());
-
-  builder->set_ct_verifier(std::move(ct_verifier));
-
-  io_thread->RegisterSTHObserver(ct_tree_tracker_.get());
-  ct_tree_tracker_unregistration_ =
-      base::Bind(&IOThread::UnregisterSTHObserver, base::Unretained(io_thread),
-                 ct_tree_tracker_.get());
-
   if (data_reduction_proxy_io_data_.get()) {
     builder->set_shared_proxy_delegate(
         data_reduction_proxy_io_data_->proxy_delegate());
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 1f960a2..8591e08b 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -53,10 +53,6 @@
 class LoadingPredictorObserver;
 }
 
-namespace certificate_transparency {
-class TreeStateTracker;
-}
-
 namespace content_settings {
 class CookieSettings;
 }
@@ -646,10 +642,6 @@
       extension_throttle_manager_;
 #endif
 
-  mutable std::unique_ptr<certificate_transparency::TreeStateTracker>
-      ct_tree_tracker_;
-  mutable base::Closure ct_tree_tracker_unregistration_;
-
   // Owned by the ChromeNetworkDelegate, which is owned (possibly with one or
   // more layers of LayeredNetworkDelegate) by the URLRequestContext, which is
   // owned by main_network_context_.
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 83530a7..5696dbaa 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -2388,104 +2388,3 @@
   ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
   ASSERT_EQ("false - not subscribed", script_result);
 }
-
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-// Push background mode is disabled by default.
-IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
-                       BackgroundModeDisabledByDefault) {
-  // Initially background mode is inactive.
-  BackgroundModeManager* background_mode_manager =
-      g_browser_process->background_mode_manager();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-
-  // Once there is a push subscription background mode is still inactive.
-  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-
-  // After dropping the last subscription it is still inactive.
-  std::string script_result;
-  base::RunLoop run_loop;
-  push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
-  ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
-  EXPECT_EQ("unsubscribe result: true", script_result);
-  // Background mode is only guaranteed to have updated once the unsubscribe
-  // callback for testing has been run (PushSubscription.unsubscribe() usually
-  // resolves before that, in order to avoid blocking on network retries etc).
-  run_loop.Run();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-}
-
-class PushMessagingBackgroundModeEnabledBrowserTest
-    : public PushMessagingBrowserTest {
- public:
-  ~PushMessagingBackgroundModeEnabledBrowserTest() override {}
-
-  // PushMessagingBrowserTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kEnablePushApiBackgroundMode);
-    PushMessagingBrowserTest::SetUpCommandLine(command_line);
-  }
-};
-
-// In this test the command line enables push background mode.
-IN_PROC_BROWSER_TEST_F(PushMessagingBackgroundModeEnabledBrowserTest,
-                       BackgroundModeEnabledWithCommandLine) {
-  // Initially background mode is inactive.
-  BackgroundModeManager* background_mode_manager =
-      g_browser_process->background_mode_manager();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-
-  // Once there is a push subscription background mode is active.
-  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
-  ASSERT_TRUE(background_mode_manager->IsBackgroundModeActive());
-
-  // Dropping the last subscription deactivates background mode.
-  std::string script_result;
-  base::RunLoop run_loop;
-  push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
-  ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
-  EXPECT_EQ("unsubscribe result: true", script_result);
-  // Background mode is only guaranteed to have updated once the unsubscribe
-  // callback for testing has been run (PushSubscription.unsubscribe() usually
-  // resolves before that, in order to avoid blocking on network retries etc).
-  run_loop.Run();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-}
-
-class PushMessagingBackgroundModeDisabledBrowserTest
-    : public PushMessagingBrowserTest {
- public:
-  ~PushMessagingBackgroundModeDisabledBrowserTest() override {}
-
-  // PushMessagingBrowserTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kDisablePushApiBackgroundMode);
-    PushMessagingBrowserTest::SetUpCommandLine(command_line);
-  }
-};
-
-// In this test the command line disables push background mode.
-IN_PROC_BROWSER_TEST_F(PushMessagingBackgroundModeDisabledBrowserTest,
-                       BackgroundModeDisabledWithCommandLine) {
-  // Initially background mode is inactive.
-  BackgroundModeManager* background_mode_manager =
-      g_browser_process->background_mode_manager();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-
-  // Once there is a push subscription background mode is still inactive.
-  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-
-  // After dropping the last subscription background mode is still inactive.
-  std::string script_result;
-  base::RunLoop run_loop;
-  push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
-  ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
-  EXPECT_EQ("unsubscribe result: true", script_result);
-  // Background mode is only guaranteed to have updated once the unsubscribe
-  // callback for testing has been run (PushSubscription.unsubscribe() usually
-  // resolves before that, in order to avoid blocking on network retries etc).
-  run_loop.Run();
-  ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
-}
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index 0e7634a..099ffc1 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -128,19 +128,6 @@
   closure.Run();
 }
 
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-bool UseBackgroundMode() {
-  // Note: if push is ever enabled in incognito, the background mode integration
-  // should not be enabled for it.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kDisablePushApiBackgroundMode))
-    return false;
-  if (command_line->HasSwitch(switches::kEnablePushApiBackgroundMode))
-    return true;
-  return base::FeatureList::IsEnabled(features::kPushMessagingBackgroundMode);
-}
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
-
 }  // namespace
 
 // static
@@ -176,21 +163,13 @@
 void PushMessagingServiceImpl::IncreasePushSubscriptionCount(int add,
                                                              bool is_pending) {
   DCHECK_GT(add, 0);
-  if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
+  if (push_subscription_count_ + pending_push_subscription_count_ == 0)
     GetGCMDriver()->AddAppHandler(kPushMessagingAppIdentifierPrefix, this);
-  }
-  if (is_pending) {
+
+  if (is_pending)
     pending_push_subscription_count_ += add;
-  } else {
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-    if (UseBackgroundMode() && g_browser_process->background_mode_manager() &&
-        !push_subscription_count_) {
-      g_browser_process->background_mode_manager()->RegisterTrigger(
-          profile_, this, false /* should_notify_user */);
-    }
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
+  else
     push_subscription_count_ += add;
-  }
 }
 
 void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
@@ -203,16 +182,9 @@
     push_subscription_count_ -= subtract;
     DCHECK_GE(push_subscription_count_, 0);
   }
-  if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
-    GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
 
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-    if (UseBackgroundMode() && g_browser_process->background_mode_manager()) {
-      g_browser_process->background_mode_manager()->UnregisterTrigger(profile_,
-                                                                      this);
-    }
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
-  }
+  if (push_subscription_count_ + pending_push_subscription_count_ == 0)
+    GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
 }
 
 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
@@ -1022,23 +994,6 @@
   HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(this);
 }
 
-// BackgroundTrigger methods ---------------------------------------------------
-base::string16 PushMessagingServiceImpl::GetName() {
-  return l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME);
-}
-
-gfx::ImageSkia* PushMessagingServiceImpl::GetIcon() {
-  return nullptr;
-}
-
-void PushMessagingServiceImpl::OnMenuClick() {
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-  chrome::ShowContentSettings(
-      BackgroundModeManager::GetBrowserWindowForProfile(profile_),
-      CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
-#endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
-}
-
 // content::NotificationObserver methods ---------------------------------------
 
 void PushMessagingServiceImpl::Observe(
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 16dca4d1..0dd1164 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -15,7 +15,6 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/background/background_trigger.h"
 #include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
 #include "chrome/common/buildflags.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
@@ -56,7 +55,6 @@
                                  public gcm::GCMAppHandler,
                                  public content_settings::Observer,
                                  public KeyedService,
-                                 public BackgroundTrigger,
                                  public content::NotificationObserver {
  public:
   // If any Service Workers are using push, starts GCM and adds an app handler.
@@ -120,11 +118,6 @@
   // KeyedService implementation.
   void Shutdown() override;
 
-  // BackgroundTrigger implementation.
-  base::string16 GetName() override;
-  gfx::ImageSkia* GetIcon() override;
-  void OnMenuClick() override;
-
   // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index c4817553b7..d9af8bd 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -130,10 +130,12 @@
 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/text_elider.h"
+#include "ui/strings/grit/ui_strings.h"
 
 #if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 #include "chrome/browser/renderer_context_menu/spelling_options_submenu_observer.h"
@@ -1406,6 +1408,11 @@
     AppendSearchProvider();
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
   }
+  if (params_.misspelled_word.empty() && ui::IsEmojiPanelSupported()) {
+    menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_EMOJI,
+                                    IDS_CONTENT_CONTEXT_EMOJI);
+    menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
+  }
 
 // 'Undo' and 'Redo' for text input with no suggestions and no text selected.
 // We make an exception for OS X as context clicking will select the closest
@@ -1736,6 +1743,9 @@
       return !!(params_.media_flags &
                 WebContextMenuData::kMediaCanPictureInPicture);
 
+    case IDC_CONTENT_CONTEXT_EMOJI:
+      return params_.is_editable;
+
     default:
       NOTREACHED();
       return false;
@@ -1753,6 +1763,9 @@
   if (id == IDC_CONTENT_CONTEXT_CONTROLS)
     return (params_.media_flags & WebContextMenuData::kMediaControls) != 0;
 
+  if (id == IDC_CONTENT_CONTEXT_EMOJI)
+    return false;
+
   // Extension items.
   if (ContextMenuMatcher::IsExtensionsCustomCommandId(id))
     return extension_items_.IsCommandIdChecked(id);
@@ -2025,6 +2038,10 @@
       ExecPictureInPicture();
       break;
 
+    case IDC_CONTENT_CONTEXT_EMOJI:
+      ui::ShowEmojiPanel();
+      break;
+
     default:
       NOTREACHED();
       break;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 405209d7..e3c6d9d7 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -73,7 +73,9 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/web/web_context_menu_data.h"
+#include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/models/menu_model.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/window_properties.h"
@@ -490,6 +492,57 @@
   ASSERT_TRUE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_COPYIMAGE));
 }
 
+IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest,
+                       ContextMenuForEmojiPanel_Enabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
+
+  content::ContextMenuParams params;
+  params.is_editable = true;
+
+  TestRenderViewContextMenu menu(
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+      params);
+  menu.Init();
+
+  EXPECT_EQ(ui::IsEmojiPanelSupported(),
+            menu.IsItemPresent(IDC_CONTENT_CONTEXT_EMOJI));
+}
+
+IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest,
+                       ContextMenuForEmojiPanel_Enabled_NonEditable) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu);
+
+  content::ContextMenuParams params;
+  params.is_editable = false;
+
+  TestRenderViewContextMenu menu(
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+      params);
+  menu.Init();
+
+  // Emoji context menu item should never be present on a non-editable field.
+  EXPECT_FALSE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_EMOJI));
+}
+
+IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest,
+                       ContextMenuForEmojiPanel_Disabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kEnableEmojiContextMenu);
+
+  content::ContextMenuParams params;
+  params.is_editable = true;
+
+  TestRenderViewContextMenu menu(
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+      params);
+  menu.Init();
+
+  // If the feature is disabled, the emoji context menu should never be present.
+  EXPECT_FALSE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_EMOJI));
+}
+
 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest, CopyLinkTextMouse) {
   std::unique_ptr<TestRenderViewContextMenu> menu = CreateContextMenu(
       GURL("http://www.google.com/"), GURL("http://www.google.com/"),
diff --git a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
index b568c07..4eac930 100644
--- a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
+++ b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
@@ -85,7 +85,7 @@
 
 }  // namespace
 
-class ChromeRenderProcessHostTest : public ExtensionBrowserTest {
+class ChromeRenderProcessHostTest : public extensions::ExtensionBrowserTest {
  public:
   ChromeRenderProcessHostTest() {}
 
diff --git a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.cc b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.cc
index 53eafce1..fd7b17dc 100644
--- a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.cc
+++ b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.cc
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/values.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
@@ -27,9 +26,6 @@
 constexpr base::TimeDelta kDefaultMeasurementInterval =
     base::TimeDelta::FromMinutes(10);
 
-base::LazyInstance<ResourceCoordinatorRenderProcessProbe>::DestructorAtExit
-    g_probe = LAZY_INSTANCE_INITIALIZER;
-
 }  // namespace
 
 ResourceCoordinatorRenderProcessProbe::RenderProcessInfo::RenderProcessInfo() =
@@ -49,7 +45,8 @@
 // static
 ResourceCoordinatorRenderProcessProbe*
 ResourceCoordinatorRenderProcessProbe::GetInstance() {
-  return g_probe.Pointer();
+  static base::NoDestructor<ResourceCoordinatorRenderProcessProbe> probe;
+  return probe.get();
 }
 
 // static
@@ -132,16 +129,24 @@
 
   content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&ResourceCoordinatorRenderProcessProbe::
-                         CollectAndDispatchRenderProcessMetricsOnIOThread,
-                     base::Unretained(this)));
+      base::BindOnce(
+          &ResourceCoordinatorRenderProcessProbe::
+              CollectRenderProcessMetricsAndStartMemoryDumpOnIOThread,
+          base::Unretained(this)));
 }
 
 void ResourceCoordinatorRenderProcessProbe::
-    CollectAndDispatchRenderProcessMetricsOnIOThread() {
+    CollectRenderProcessMetricsAndStartMemoryDumpOnIOThread() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(is_gathering_);
 
+  // Dispatch the memory collection request.
+  memory_instrumentation::MemoryInstrumentation::GetInstance()
+      ->RequestGlobalDump(
+          base::BindRepeating(&ResourceCoordinatorRenderProcessProbe::
+                                  ProcessGlobalMemoryDumpAndDispatchOnIOThread,
+                              base::Unretained(this)));
+
   RenderProcessInfoMap::iterator iter = render_process_info_map_.begin();
   while (iter != render_process_info_map_.end()) {
     auto& render_process_info = iter->second;
@@ -157,12 +162,21 @@
       continue;
     }
   }
+}
 
+void ResourceCoordinatorRenderProcessProbe::
+    ProcessGlobalMemoryDumpAndDispatchOnIOThread(
+        bool global_success,
+        std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump) {
   // Create the measurement batch.
   mojom::ProcessResourceMeasurementBatchPtr batch =
       mojom::ProcessResourceMeasurementBatch::New();
 
-  for (auto& render_process_info_map_entry : render_process_info_map_) {
+  // TODO(siggi): Add start/end times. This will need some support from
+  //     the memory_instrumentation code.
+
+  // Start by adding the render process hosts we know about to the batch.
+  for (const auto& render_process_info_map_entry : render_process_info_map_) {
     auto& render_process_info = render_process_info_map_entry.second;
     // TODO(oysteine): Move the multiplier used to avoid precision loss
     // into a shared location, when this property gets used.
@@ -171,13 +185,35 @@
 
     measurement->pid = render_process_info.process.Pid();
     measurement->cpu_usage = render_process_info.cpu_usage;
-    // TODO(siggi): Add the private footprint.
 
     batch->measurements.push_back(std::move(measurement));
   }
 
-  bool should_restart = DispatchMetrics(std::move(batch));
+  if (dump) {
+    // Then amend the ones we have memory metrics for with their private
+    // footprint. The global dump may contain non-renderer processes, it may
+    // contain renderer processes we didn't capture at the start of the cycle,
+    // and it may not contain all the renderer processes we know about.
+    // This may happen due to the inherent race between the request and
+    // starting/stopping renderers, or because of other failures
+    // This may therefore provide incomplete information.
+    for (const auto& dump_entry : dump->process_dumps()) {
+      base::ProcessId pid = dump_entry.pid();
 
+      for (const auto& measurement : batch->measurements) {
+        if (measurement->pid == pid) {
+          measurement->private_footprint_kb =
+              dump_entry.os_dump().private_footprint_kb;
+          break;
+        }
+      }
+    }
+  } else {
+    // We should only get a nullptr in case of failure.
+    DCHECK(!global_success);
+  }
+
+  bool should_restart = DispatchMetrics(std::move(batch));
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
       base::BindOnce(
diff --git a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h
index 7849d48..057ec82 100644
--- a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h
+++ b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe.h
@@ -9,11 +9,12 @@
 #include <memory>
 #include <utility>
 
-#include "base/lazy_instance.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/process/process.h"
 #include "base/process/process_metrics.h"
 #include "base/timer/timer.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 #include "services/resource_coordinator/public/cpp/system_resource_coordinator.h"
 
 namespace resource_coordinator {
@@ -41,6 +42,7 @@
   void StartSingleGather();
 
  protected:
+  // Internal state protected for testing.
   struct RenderProcessInfo {
     RenderProcessInfo();
     ~RenderProcessInfo();
@@ -51,9 +53,7 @@
   };
   using RenderProcessInfoMap = std::map<int, RenderProcessInfo>;
 
-  // Internal state protected for testing.
-  friend struct base::LazyInstanceTraitsBase<
-      ResourceCoordinatorRenderProcessProbe>;
+  friend class base::NoDestructor<ResourceCoordinatorRenderProcessProbe>;
 
   ResourceCoordinatorRenderProcessProbe();
   virtual ~ResourceCoordinatorRenderProcessProbe();
@@ -61,10 +61,13 @@
   // (1) Identify all of the render processes that are active to measure.
   // Child render processes can only be discovered in the browser's UI thread.
   void RegisterAliveRenderProcessesOnUIThread();
-  // (2) Collect and dispatch the render process metrics to the system
-  // coordination unit.
-  void CollectAndDispatchRenderProcessMetricsOnIOThread();
-  // (3) Initiate the next render process metrics collection cycle if the
+  // (2) Collect the render process CPU metrics and initiate a memory dump.
+  void CollectRenderProcessMetricsAndStartMemoryDumpOnIOThread();
+  // (3) Process the results of the memory dump and dispatch the results.
+  void ProcessGlobalMemoryDumpAndDispatchOnIOThread(
+      bool success,
+      std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);
+  // (4) Initiate the next render process metrics collection cycle if the
   // cycle has been started and |restart_cycle| is true, which consists of a
   // delayed call to perform (1) via a timer.
   // Virtual for testing.
diff --git a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe_browsertest.cc b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe_browsertest.cc
index 80d9718..92cdfe5c 100644
--- a/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe_browsertest.cc
+++ b/chrome/browser/resource_coordinator/resource_coordinator_render_process_probe_browsertest.cc
@@ -102,6 +102,16 @@
   ResourceCoordinatorRenderProcessProbeBrowserTest() = default;
   ~ResourceCoordinatorRenderProcessProbeBrowserTest() override = default;
 
+  static bool AtLeastOneMemoryMeasurementIsNonZero(
+      const mojom::ProcessResourceMeasurementBatchPtr& batch) {
+    for (const auto& measurement : batch->measurements) {
+      if (measurement->private_footprint_kb > 0)
+        return true;
+    }
+
+    return false;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorRenderProcessProbeBrowserTest);
 };
@@ -138,6 +148,11 @@
   for (const auto& measurement : probe.last_measurement_batch()->measurements)
     EXPECT_EQ(0.0, measurement->cpu_usage);
 
+  // There is an inherent race in memory measurement that may cause failures
+  // which will result in zero private footprint returns. To work around this,
+  // assert that there is at least one non-zero measurement.
+  AtLeastOneMemoryMeasurementIsNonZero(probe.last_measurement_batch());
+
   // Open a second tab and complete a navigation.
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), embedded_test_server()->GetURL("/title1.html"),
@@ -163,10 +178,14 @@
 
   size_t info_map_size = info_map.size();
   probe.StartGatherCycleAndWait();
-  // The second and subsequent CPU measurements should return some data.
+  // The second and subsequent CPU measurements should return some data,
+  // although the measurement granularity on some OSen is such that zero returns
+  // are almost certain.
   for (const auto& measurement : probe.last_measurement_batch()->measurements)
     EXPECT_LE(0.0, measurement->cpu_usage);
 
+  AtLeastOneMemoryMeasurementIsNonZero(probe.last_measurement_batch());
+
   EXPECT_EQ(info_map_size, info_map.size());
   for (const auto& entry : probe.render_process_info_map()) {
     const int key = entry.first;
diff --git a/chrome/browser/resources/PRESUBMIT.py b/chrome/browser/resources/PRESUBMIT.py
index 6767998..a8156d4 100644
--- a/chrome/browser/resources/PRESUBMIT.py
+++ b/chrome/browser/resources/PRESUBMIT.py
@@ -8,8 +8,6 @@
 for more details about the presubmit API built into depot_tools.
 """
 
-import os
-
 ACTION_XML_PATH = '../../../tools/metrics/actions/actions.xml'
 
 
@@ -125,8 +123,8 @@
     results += CheckHtml(input_api, output_api)
 
   webui_sources = set(['optimize_webui.py', 'unpack_pak.py'])
-  affected_filenames = set([os.path.basename(f.LocalPath()) for f in affected])
-  if webui_sources.intersection(affected_filenames):
+  affected_files = [input_api.os_path.basename(f.LocalPath()) for f in affected]
+  if webui_sources.intersection(set(affected_files)):
     results += RunOptimizeWebUiTests(input_api, output_api)
   results += _CheckWebDevStyle(input_api, output_api)
   results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api,
diff --git a/chrome/browser/resources/chromeos/arc_support/main.html b/chrome/browser/resources/chromeos/arc_support/main.html
index ee3b3bb..30ab6322 100644
--- a/chrome/browser/resources/chromeos/arc_support/main.html
+++ b/chrome/browser/resources/chromeos/arc_support/main.html
@@ -8,9 +8,9 @@
   <link rel="stylesheet" href="chrome://resources/css/controlled_indicator.css">
   <link rel="stylesheet" href="chrome://resources/css/overlay.css">
   <link rel="import" href="chrome://resources/html/polymer.html">
+  <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-  <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
   <style is="custom-style" include="iron-flex iron-flex-alignment">
     paper-button {
       border-radius: 4px;
@@ -49,13 +49,6 @@
       background-color: rgb(66, 133, 244); /* #4285f4 */
       color: white;
     }
-    paper-checkbox {
-      --paper-checkbox-size: 12px;
-      --paper-checkbox-checked-color: rgb(66, 133, 244); /* #4285f4 */
-      --paper-checkbox-ink-size: 36px;
-      --paper-checkbox-label-spacing: 20px;
-      --paper-checkbox-vertical-align: top;
-    }
   </style>
   <link rel="stylesheet" href="main.css">
   <link rel="stylesheet" href="progressbar.css">
@@ -108,26 +101,26 @@
           </div>
           <div id="metrics-preference">
             <label class="layout horizontal section-checkbox-container">
-              <paper-checkbox class="checkbox-option">
+              <cr-checkbox class="checkbox-option">
                 <p class="checkbox-text"></p>
-              </paper-checkbox>
+              </cr-checkbox>
               <p class="content-text"></p>
             </label>
           </div>
           <div id="backup-restore-preference">
             <label class="layout horizontal section-checkbox-container">
-              <paper-checkbox class="checkbox-option">
+              <cr-checkbox class="checkbox-option">
                 <p class="checkbox-text"
                     i18n-values=".innerHTML:textBackupRestore"></p>
-              </paper-checkbox>
+              </cr-checkbox>
             </label>
           </div>
           <div id="location-service-preference">
             <label class="layout horizontal section-checkbox-container">
-              <paper-checkbox class="checkbox-option">
+              <cr-checkbox class="checkbox-option">
                 <p class="checkbox-text"
                     i18n-values=".innerHTML:textLocationService"></p>
-              </paper-checkbox>
+              </cr-checkbox>
             </label>
           </div>
           <div id="pai-service-descirption">
diff --git a/chrome/browser/resources/chromeos/emulator/audio_settings.html b/chrome/browser/resources/chromeos/emulator/audio_settings.html
index 966f6bd0..91ab575f 100644
--- a/chrome/browser/resources/chromeos/emulator/audio_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/audio_settings.html
@@ -1,10 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-button/paper-radio-button.html">
@@ -32,13 +33,13 @@
           </div>
           <div class="form-field-section">
             <span class="toggle-button-label">Is Input</span>
-            <paper-checkbox
-                checked="{{currentEditableObject.isInput}}"></paper-checkbox>
+            <cr-checkbox
+                checked="{{currentEditableObject.isInput}}"></cr-checkbox>
           </div>
           <div class="form-field-section">
             <span class="toggle-button-label">Active</span>
-            <paper-checkbox
-                checked="{{currentEditableObject.active}}"></paper-checkbox>
+            <cr-checkbox
+                checked="{{currentEditableObject.active}}"></cr-checkbox>
           </div>
           <div class="form-field-section">
             <div class="form-label">Audio Type</div>
@@ -88,12 +89,12 @@
                 </paper-icon-button>
               </td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.isInput}}"
-                    on-change="insertAudioNode"></paper-checkbox>
+                <cr-checkbox checked="{{item.isInput}}"
+                    on-change="insertAudioNode"></cr-checkbox>
               </td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.active}}"
-                    on-change="insertAudioNode"></paper-checkbox>
+                <cr-checkbox checked="{{item.active}}"
+                    on-change="insertAudioNode"></cr-checkbox>
               </td>
             </tr>
           </template>
diff --git a/chrome/browser/resources/chromeos/emulator/battery_settings.html b/chrome/browser/resources/chromeos/emulator/battery_settings.html
index 8454f6b..9c82510 100644
--- a/chrome/browser/resources/chromeos/emulator/battery_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/battery_settings.html
@@ -1,9 +1,10 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-button/paper-radio-button.html">
@@ -60,7 +61,7 @@
             <tr>
               <td class="alias-cell">[[item.name]]</td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.connected}}"></paper-checkbox>
+                <cr-checkbox checked="{{item.connected}}"></cr-checkbox>
               </td>
               <td class="control-cell">
                 <paper-button on-tap="onSetAsSourceTap"
diff --git a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
index e804c7b..f70afe59 100644
--- a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
@@ -1,10 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-button/paper-radio-button.html">
@@ -38,13 +39,13 @@
           </div>
           <div class="form-field-section">
             <span class="toggle-button-label">Trusted</span>
-            <paper-checkbox
-                checked="{{currentEditableObject.isTrusted}}"></paper-checkbox>
+            <cr-checkbox
+                checked="{{currentEditableObject.isTrusted}}"></cr-checkbox>
           </div>
           <div class="form-field-section">
             <span class="toggle-button-label">Incoming Connnection</span>
-            <paper-checkbox
-                checked="{{currentEditableObject.incoming}}"></paper-checkbox>
+            <cr-checkbox
+                checked="{{currentEditableObject.incoming}}"></cr-checkbox>
           </div>
           <div class="form-field-section">
             <div class="form-label">Class</div>
@@ -126,13 +127,13 @@
                 </paper-icon-button>
               </td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.discoverable}}"
+                <cr-checkbox checked="{{item.discoverable}}"
                     data-predefined="true"
-                    on-change="discoverDevice"></paper-checkbox>
+                    on-change="discoverDevice"></cr-checkbox>
               </td>
               <td class="control-cell">
-                <paper-checkbox on-change="pairDevice" data-predefined="true"
-                    checked="{{item.paired}}"></paper-checkbox>
+                <cr-checkbox on-change="pairDevice" data-predefined="true"
+                    checked="{{item.paired}}"></cr-checkbox>
               </td>
             </tr>
           </template>
@@ -156,14 +157,14 @@
                 </paper-icon-button>
               </td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.discoverable}}"
+                <cr-checkbox checked="{{item.discoverable}}"
                     data-predefined="false"
-                    on-change="discoverDevice"></paper-checkbox>
+                    on-change="discoverDevice"></cr-checkbox>
               </td>
               <td class="control-cell">
-                <paper-checkbox checked="{{item.paired}}"
+                <cr-checkbox checked="{{item.paired}}"
                     data-predefined="false"
-                    on-change="pairDevice"></paper-checkbox>
+                    on-change="pairDevice"></cr-checkbox>
               </td>
             </tr>
           </template>
diff --git a/chrome/browser/resources/chromeos/emulator/shared_styles.html b/chrome/browser/resources/chromeos/emulator/shared_styles.html
index 81b57e8..bdf06ea39 100644
--- a/chrome/browser/resources/chromeos/emulator/shared_styles.html
+++ b/chrome/browser/resources/chromeos/emulator/shared_styles.html
@@ -9,10 +9,6 @@
         width: 200px;
       }
 
-      #editModal paper-checkbox {
-        -webkit-margin-start: 15px;
-      }
-
       paper-radio-button {
         display: inline-block;
       }
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.css b/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
index 76ceff1..ad8d4af 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
@@ -54,14 +54,6 @@
   padding: 0 20px 16px 20px;
 }
 
-#arc-enable-backup-restore,
-#arc-enable-location-service {
-  --paper-checkbox-size: 12px;
-  --paper-checkbox-checked-color: rgb(66, 133, 244); /* #4285f4 */
-  --paper-checkbox-label-spacing: 20px;
-  --paper-checkbox-vertical-align: top;
-}
-
 #arc-policy-link {
   margin: 0;
   padding: 16px 0 16px 20px;
@@ -71,7 +63,6 @@
   color: rgba(0, 0, 0, 0.54);
 }
 
-
 #google-service-confirmation-text {
   font-weight: 600;
 }
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
index e573241c..d493de2 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
@@ -2,11 +2,10 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
-
 <dom-module id="arc-tos-root">
   <template>
     <link rel="stylesheet" href="arc_terms_of_service.css">
@@ -31,15 +30,15 @@
           <p id="arc-text-metrics"></p>
         </div>
         <div class="parameter-section arc-tos-content">
-          <paper-checkbox id="arc-enable-backup-restore">
+          <cr-checkbox id="arc-enable-backup-restore">
             <p i18n-values=".innerHTML:arcTextBackupRestore"></p>
-          </paper-checkbox>
+          </cr-checkbox>
         </div>
         <div id= "arc-location-service"
             class="parameter-section arc-tos-content">
-          <paper-checkbox id="arc-enable-location-service">
+          <cr-checkbox id="arc-enable-location-service">
             <p i18n-values=".innerHTML:arcTextLocationService"></p>
-          </paper-checkbox>
+          </cr-checkbox>
         </div>
         <div id="arc-pai-service" class="parameter-section arc-tos-content">
           <p i18n-values=".innerHTML:arcTextPaiService"></p>
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.css b/chrome/browser/resources/chromeos/login/gaia_input.css
index 419ac1b..58a371b 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.css
+++ b/chrome/browser/resources/chromeos/login/gaia_input.css
@@ -26,6 +26,10 @@
   color: rgba(0, 0, 0, 0.54);
 }
 
+iron-input input {
+  @apply --paper-input-container-shared-input-style;
+}
+
 :host-context(html[dir=rtl]) input {
   flex-direction: row-reverse;
 }
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.html b/chrome/browser/resources/chromeos/login/gaia_input.html
index 305be84..460c8a9b 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.html
+++ b/chrome/browser/resources/chromeos/login/gaia_input.html
@@ -44,10 +44,13 @@
     <paper-input-container id="decorator" on-tap="onTap"
         invalid="[[isInvalid]]" disabled$="[[disabled]]">
       <label slot="label"><span>[[label]]</span></label>
-      <input id="input" is="iron-input" on-keydown="onKeyDown"
-          bind-value="{{value}}" invalid="[[isInvalid]]" slot="input"
+      <iron-input id="ironInput" slot="input" class="flex"
+          bind-value="{{value}}">
+        <input id="input" on-keydown="onKeyDown"
+          value="{{value::input}}" invalid="[[isInvalid]]"
           required$="[[required]]" disabled$="[[disabled]]"
-          pattern$="[[pattern]]" class="flex">
+          pattern$="[[pattern]]">
+      </iron-input>
       <span id="domainLabel" slot="suffix">[[domain]]</span>
       <template is="dom-if" if="[[error]]">
         <paper-input-error slot="add-on">[[error]]</paper-input-error>
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.js b/chrome/browser/resources/chromeos/login/gaia_input.js
index f04d94e31..a273b57 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.js
+++ b/chrome/browser/resources/chromeos/login/gaia_input.js
@@ -49,7 +49,7 @@
     },
 
     checkValidity: function() {
-      var valid = this.$.input.validate();
+      var valid = this.$.ironInput.validate();
       this.isInvalid = !valid;
       return valid;
     },
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.css b/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
index 4ada0f6..cabd813 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.css
@@ -13,7 +13,8 @@
   width: inherit;
 }
 
-paper-toggle-button {
+cr-toggle {
+  align-self: center;
   width: 36px;
 }
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
index b48fcde8..f5b51957 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 
 <dom-module id="oobe-a11y-option">
   <template>
@@ -18,9 +19,9 @@
           <content select=".unchecked-value"></content>
         </div>
       </div>
-      <paper-toggle-button id="button" checked="{{checked}}"
+      <cr-toggle id="button" checked="{{checked}}"
           aria-label$="[[labelForAria]]">
-      </paper-toggle-button>
+      </cr-toggle>
     </div>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.js b/chrome/browser/resources/chromeos/login/oobe_a11y_option.js
index 2596b63e..b76b6480 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.js
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.js
@@ -7,18 +7,14 @@
 
   properties: {
     /**
-     * If paper-toggle-button is checked.
+     * If cr-toggle is checked.
      */
-    checked: {
-      type: Boolean,
-    },
+    checked: Boolean,
 
     /**
      * Chrome message handling this option.
      */
-    chromeMessage: {
-      type: String,
-    },
+    chromeMessage: String,
 
     /**
      * ARIA-label for the button.
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index 45a1d63..844078c5 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -4,7 +4,6 @@
 
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <iron-iconset-svg name="oobe-eula-32" size="32">
diff --git a/chrome/browser/resources/chromeos/login/oobe_hid_detection.html b/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
index 61937527..610ae53 100644
--- a/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
+++ b/chrome/browser/resources/chromeos/login/oobe_hid_detection.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <iron-iconset-svg name="oobe-hid-detection" size="24">
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.css b/chrome/browser/resources/chromeos/login/oobe_reset.css
index a8bcec2a..3455da8 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.css
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.css
@@ -36,8 +36,6 @@
 }
 
 #tpmFirmwareUpdateCheckbox {
-  --paper-checkbox-size: 16px;
-  --paper-checkbox-checked-color: var(--google-blue-500);
   margin-top: 2px;
   size: 16px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.html b/chrome/browser/resources/chromeos/login/oobe_reset.html
index db90477..474f227 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.html
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.html
@@ -2,9 +2,9 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 
 <iron-iconset-svg name="oobe-reset-32" size="32">
   <svg>
@@ -68,11 +68,11 @@
             i18n-values="alt:resetScreenIllustrationTitle">
         <div id="tpmFirmwareUpdate" class="layout horizontal"
             hidden="[[!tpmFirmwareUpdateAvailable_]]">
-          <paper-checkbox id="tpmFirmwareUpdateCheckbox"
+          <cr-checkbox id="tpmFirmwareUpdateCheckbox"
               checked="{{tpmFirmwareUpdateChecked_}}"
-              disabled="{{!tpmFirmwareUpdateEditable_}}"
+              disabled="[[!tpmFirmwareUpdateEditable_]]"
               on-change="onTPMFirmwareUpdateChanged_">
-          </paper-checkbox>
+          </cr-checkbox>
           <div id="tpmFirmwareUpdateContainer">
             <span i18n-content="resetTPMFirmwareUpdate"></span>
             <a href="#" hidden="[[isHelpLinkHidden_(uiState_, isOfficial_)]]"
diff --git a/chrome/browser/resources/chromeos/login/recommend_apps.html b/chrome/browser/resources/chromeos/login/recommend_apps.html
index 2d30a9669305..15f73ba 100644
--- a/chrome/browser/resources/chromeos/login/recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/recommend_apps.html
@@ -3,8 +3,8 @@
      found in the LICENSE file. -->
 
 <link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 
 <dom-module id="recommend-apps">
   <template>
@@ -27,12 +27,12 @@
       <div id="recommend-apps-container" class="footer flex layout horizontal">
         <template is="dom-repeat" items="{{apps}}">
           <div class="column">
-            <paper-checkbox>
+            <cr-checkbox>
               <div class="checkbox-label">
                 <img class="app-icon" src="{{item.icon}}">
                 <span class="app-title">{{item.name}}</span>
               </div>
-            </paper-checkbox>
+            </cr-checkbox>
           </div>
         </template>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/sync_consent.css b/chrome/browser/resources/chromeos/login/sync_consent.css
index 4e1fe364..1fffd43 100644
--- a/chrome/browser/resources/chromeos/login/sync_consent.css
+++ b/chrome/browser/resources/chromeos/login/sync_consent.css
@@ -33,9 +33,10 @@
   margin-bottom: 4px;
 }
 
-paper-checkbox {
-  --paper-checkbox-label-spacing: 12px;
-  --paper-checkbox-size: 16px;
+cr-checkbox {
+  --cr-checkbox-label-container: {
+    -webkit-padding-start: 12px;
+  };
   -webkit-padding-start: 8px;
   padding-top: 28px; /* 40 to label base line => 40 - 16/2 - 13/3 = 28 */
 }
diff --git a/chrome/browser/resources/chromeos/login/sync_consent.html b/chrome/browser/resources/chromeos/login/sync_consent.html
index 7bceff0..c7da728bd 100644
--- a/chrome/browser/resources/chromeos/login/sync_consent.html
+++ b/chrome/browser/resources/chromeos/login/sync_consent.html
@@ -2,6 +2,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
@@ -81,9 +82,9 @@
             </div>
           </div>
         </div>
-        <paper-checkbox id="reviewSettingsBox">
+        <cr-checkbox id="reviewSettingsBox">
           [[i18nDynamic(locale, 'syncConsentReviewSyncOptionsText')]]
-        </paper-checkbox>
+        </cr-checkbox>
       </div>
       <div class="bottom-buttons layout horizontal end-justified">
         <oobe-text-button id="acceptAndContinue"
diff --git a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
index bf7ab0b..a5d8368 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
@@ -7,14 +7,49 @@
 var RoleType = chrome.automation.RoleType;
 var SelectToSpeakState = chrome.accessibilityPrivate.SelectToSpeakState;
 
-// CrosSelectToSpeakStartSpeechMethod enums.
-// These values are persited to logs and should not be renumbered or re-used.
-// See tools/metrics/histograms/enums.xml.
-const START_SPEECH_METHOD_MOUSE = 0;
-const START_SPEECH_METHOD_KEYSTROKE = 1;
-// The number of enum values in CrosSelectToSpeapStartSpeechMethod. This should
-// be kept in sync with the enum count in tools/metrics/histograms/enums.xml.
-const START_SPEECH_METHOD_COUNT = 2;
+/**
+ * CrosSelectToSpeakStartSpeechMethod enums.
+ * These values are persisted to logs and should not be renumbered or re-used.
+ * See tools/metrics/histograms/enums.xml.
+ * @enum {number}
+ */
+const StartSpeechMethod = {
+  MOUSE: 0,
+  KEYSTROKE: 1,
+};
+
+/**
+ * The number of enum values in CrosSelectToSpeakStartSpeechMethod. This should
+ * be kept in sync with the enum count in tools/metrics/histograms/enums.xml.
+ * @type {number}
+ */
+const START_SPEECH_METHOD_COUNT = Object.keys(StartSpeechMethod).length;
+
+/**
+ * CrosSelectToSpeakStateChangeEvent enums.
+ * These values are persisted to logs and should not be renumbered or re-used.
+ * See tools/metrics/histograms/enums.xml.
+ * @enum {number}
+ */
+const StateChangeEvent = {
+  START_SELECTION: 0,
+  CANCEL_SPEECH: 1,
+  CANCEL_SELECTION: 2,
+};
+
+/**
+ * The number of enum values in CrosSelectToSpeakStateChangeEvent. This should
+ * be kept in sync with the enum count in tools/metrics/histograms/enums.xml.
+ * @type {number}
+ */
+const STATE_CHANGE_EVENT_COUNT = Object.keys(StateChangeEvent).length;
+
+/**
+ * The name of the state change request metric.
+ * @type {string}
+ */
+const STATE_CHANGE_EVENT_METRIC_NAME =
+    'Accessibility.CrosSelectToSpeak.SelectToSpeakStateChangeRequested';
 
 // This must be the same as in ash/system/accessibility/select_to_speak_tray.cc:
 // ash::kSelectToSpeakTrayClassName.
@@ -329,7 +364,7 @@
         return;
       }
       this.startSpeechQueue_(nodes);
-      this.recordStartEvent_(START_SPEECH_METHOD_MOUSE);
+      this.recordStartEvent_(StartSpeechMethod.MOUSE);
     }.bind(this));
   },
 
@@ -541,7 +576,7 @@
       return;
     }
     this.initializeScrollingToOffscreenNodes_(focusedNode.root);
-    this.recordStartEvent_(START_SPEECH_METHOD_KEYSTROKE);
+    this.recordStartEvent_(StartSpeechMethod.KEYSTROKE);
   },
 
   /**
@@ -643,21 +678,26 @@
     // We will need to track the current state and toggle from one state to
     // the next when this function is called, and then call
     // accessibilityPrivate.onSelectToSpeakStateChanged with the new state.
-    // TODO(katie): Add metrics for state transition requests.
     switch (this.state_) {
       case SelectToSpeakState.INACTIVE:
         // Start selection.
         this.trackingMouse_ = true;
         this.onStateChanged_(SelectToSpeakState.SELECTING);
+        this.recordSelectToSpeakStateChangeEvent_(
+            StateChangeEvent.START_SELECTION);
         break;
       case SelectToSpeakState.SPEAKING:
         // Stop speaking.
         this.cancelIfSpeaking_(true /* clear the focus ring */);
+        this.recordSelectToSpeakStateChangeEvent_(
+            StateChangeEvent.CANCEL_SPEECH);
         break;
       case SelectToSpeakState.SELECTING:
         // Cancelled selection.
         this.trackingMouse_ = false;
         this.onStateChanged_(SelectToSpeakState.INACTIVE);
+        this.recordSelectToSpeakStateChangeEvent_(
+            StateChangeEvent.CANCEL_SELECTION);
     }
   },
 
@@ -947,6 +987,16 @@
   },
 
   /**
+   * Records a user-requested state change event from a given state.
+   * @param {number} changeType
+   */
+  recordSelectToSpeakStateChangeEvent_: function(changeType) {
+    chrome.metricsPrivate.recordEnumerationValue(
+        'Accessibility.CrosSelectToSpeak.SelectToSpeakStateChangeRequested',
+        changeType, STATE_CHANGE_EVENT_COUNT);
+  },
+
+  /**
    * Loads preferences from chrome.storage, sets default values if
    * necessary, and registers a listener to update prefs when they
    * change.
diff --git a/chrome/browser/resources/chromeos/zip_archiver/css/passphrase-dialog.css b/chrome/browser/resources/chromeos/zip_archiver/css/passphrase-dialog.css
index dc9dc11..39346f4a 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/css/passphrase-dialog.css
+++ b/chrome/browser/resources/chromeos/zip_archiver/css/passphrase-dialog.css
@@ -3,8 +3,7 @@
  * found in the LICENSE file. */
 
 input,
-paper-button,
-paper-checkbox {
+paper-button {
   -webkit-app-region: no-drag;
 }
 
@@ -29,11 +28,6 @@
   line-height: inherit;
 }
 
-#remember {
-  --paper-checkbox-checked-color: rgb(51, 103, 214);
-  font-size: 13px;
-}
-
 #title {
   color: black;
   font-size: 14px;
@@ -61,10 +55,6 @@
   box-shadow: 0 0 0 2px rgba(51, 103, 214, 0.5);
 }
 
-paper-checkbox {
-  --paper-checkbox-ink-size: 38px;
-}
-
 paper-input-container {
   --paper-input-container-focus-color: rgb(51, 103, 214);
   --paper-input-container-underline-focus: {
diff --git a/chrome/browser/resources/chromeos/zip_archiver/html/passphrase-dialog.html b/chrome/browser/resources/chromeos/zip_archiver/html/passphrase-dialog.html
index 664e146f..c1a5806e5 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/html/passphrase-dialog.html
+++ b/chrome/browser/resources/chromeos/zip_archiver/html/passphrase-dialog.html
@@ -1,6 +1,5 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
 
 <dom-module id="passphrase-dialog">
diff --git a/chrome/browser/resources/md_extensions/kiosk_dialog.html b/chrome/browser/resources/md_extensions/kiosk_dialog.html
index 5fe53138..0a7a4c8 100644
--- a/chrome/browser/resources/md_extensions/kiosk_dialog.html
+++ b/chrome/browser/resources/md_extensions/kiosk_dialog.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
@@ -9,7 +10,6 @@
 <link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="item_behavior.html">
@@ -120,11 +120,11 @@
             $i18n{add}
           </paper-button>
         </div>
-        <paper-checkbox disabled="[[!canEditBailout_]]"
-            on-pointerdown="onBailoutTap_" checked="[[bailoutDisabled_]]"
+        <cr-checkbox disabled="[[!canEditBailout_]]" id="bailout"
+            on-change="onBailoutChanged_" checked="[[bailoutDisabled_]]"
             hidden="[[!canEditAutoLaunch_]]">
           $i18n{kioskDisableBailout}
-        </paper-checkbox>
+        </cr-checkbox>
       </div>
       <div slot="button-container">
         <paper-button class="action-button" on-click="onDoneTap_">
diff --git a/chrome/browser/resources/md_extensions/kiosk_dialog.js b/chrome/browser/resources/md_extensions/kiosk_dialog.js
index 66ada3f..49e5056 100644
--- a/chrome/browser/resources/md_extensions/kiosk_dialog.js
+++ b/chrome/browser/resources/md_extensions/kiosk_dialog.js
@@ -123,26 +123,25 @@
      * @param {!Event} event
      * @private
      */
-    onBailoutTap_: function(event) {
+    onBailoutChanged_: function(event) {
       event.preventDefault();
-      if (this.bailoutDisabled_) {
-        this.kioskBrowserProxy_.setDisableBailoutShortcut(false);
-        this.bailoutDisabled_ = false;
-        this.$['confirm-dialog'].close();
-      } else {
+      if (this.$.bailout.checked) {
         this.$['confirm-dialog'].showModal();
+      } else {
+        this.kioskBrowserProxy_.setDisableBailoutShortcut(false);
+        this.$['confirm-dialog'].close();
       }
     },
 
     /** @private */
     onBailoutDialogCancelTap_: function() {
+      this.$.bailout.checked = false;
       this.$['confirm-dialog'].cancel();
     },
 
     /** @private */
     onBailoutDialogConfirmTap_: function() {
       this.kioskBrowserProxy_.setDisableBailoutShortcut(true);
-      this.bailoutDisabled_ = true;
       this.$['confirm-dialog'].close();
     },
 
diff --git a/chrome/browser/resources/md_user_manager/create_profile.html b/chrome/browser/resources/md_user_manager/create_profile.html
index e63aa7a..c2ad589 100644
--- a/chrome/browser/resources/md_user_manager/create_profile.html
+++ b/chrome/browser/resources/md_user_manager/create_profile.html
@@ -2,9 +2,9 @@
 
 <link rel="import" href="/profile_browser_proxy.html">
 <link rel="import" href="/shared_styles.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
@@ -13,14 +13,13 @@
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 
 <dom-module id="create-profile">
   <template>
     <style
-        include="shared-styles iron-positioning md-select paper-checkbox-style">
+        include="shared-styles iron-positioning md-select">
       :host {
         align-self: center;
       }
@@ -90,18 +89,10 @@
         width: 300px;
       }
 
-      paper-checkbox {
-        --paper-checkbox-checked-color: var(--google-blue-500);
-        --paper-checkbox-label-spacing: 16px;
-        --paper-checkbox-size: 16px;
-        --paper-checkbox-unchecked-color: var(--paper-grey-600);
+      cr-checkbox {
         margin-top: 24px;
       }
 
-      paper-checkbox + paper-checkbox {
-        margin-top: 16px;
-      }
-
       #actions {
         bottom: 16px;
         display: flex;
@@ -133,10 +124,10 @@
       <cr-profile-avatar-selector avatars="[[availableIcons_]]"
           selected-avatar="{{selectedAvatar_}}">
       </cr-profile-avatar-selector>
-      <paper-checkbox id="createShortcutCheckbox" checked="{{createShortcut_}}"
+      <cr-checkbox id="createShortcutCheckbox" checked="{{createShortcut_}}"
           hidden="[[!isProfileShortcutsEnabled_]]">
         $i18n{createDesktopShortcutLabel}
-      </paper-checkbox>
+      </cr-checkbox>
       <div id="actions">
         <paper-spinner-lite active="[[isSpinnerActive_(createInProgress_)]]">
         </paper-spinner-lite>
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
index 9cc5423..fa2d57b 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
@@ -117,11 +117,10 @@
   text-align: center;
 }
 
-paper-checkbox {
-  --paper-checkbox-checked-color: white;
-  --paper-checkbox-checkmark-color: var(--paper-blue-700);
-  --paper-checkbox-ink-size: 35px;
-  --paper-checkbox-unchecked-color: white;
+cr-checkbox {
+  --cr-checkbox-checked-box-color: white;
+  --cr-checkbox-unchecked-box-color: white;
+  --cr-checkbox-mark-color: var(--paper-blue-700);
 }
 
 paper-item {
diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
index 6b606df..fc0008c 100644
--- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
+++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-listbox/paper-listbox.html">
@@ -28,8 +29,7 @@
         <div id="first-run-flow-cloud-pref"
             hidden$="[[!showFirstRunFlowCloudPref]]">
           <div>
-            <paper-checkbox checked id="first-run-cloud-checkbox">
-            </paper-checkbox>
+            <cr-checkbox checked id="first-run-cloud-checkbox"></cr-checkbox>
           </div>
           <div>
             <span>[[getFirstRunFlowCloudPrefText_()]]</span>
diff --git a/chrome/browser/resources/media_router/elements/route_controls/BUILD.gn b/chrome/browser/resources/media_router/elements/route_controls/BUILD.gn
index 8ff292f..0b21078 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/BUILD.gn
+++ b/chrome/browser/resources/media_router/elements/route_controls/BUILD.gn
@@ -24,6 +24,5 @@
 js_library("route_controls_interface") {
   deps = [
     "../..:media_router_data",
-    "//third_party/polymer/v1_0/components-chromium/paper-checkbox:paper-checkbox-extracted",
   ]
 }
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
index 322ee273..56ee529f 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
@@ -79,10 +79,6 @@
   padding: 0.3em 0;
 }
 
-paper-checkbox {
-  --paper-checkbox-checked-color: #1976D2;
-}
-
 #hangouts-local-present-controls {
   cursor: pointer;
   display: inline-block;
@@ -91,10 +87,6 @@
   white-space: nowrap;
 }
 
-#hangouts-local-present-checkbox {
-  --paper-checkbox-vertical-align: top;
-};
-
 #hangouts-local-present-checkbox-subtitle {
   display: block;
   font-size: 0.8em;
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
index 65a26cf..788339f 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
@@ -1,8 +1,9 @@
-<link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
+<link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/av-icons.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html">
 <dom-module id="route-controls">
   <link rel="import" type="css" href="../../media_router_common.css">
@@ -70,7 +71,7 @@
           </span>
           <div id="hangouts-local-present-controls"
                hidden="[[!routeStatus.hangoutsExtraData]]">
-            <paper-checkbox
+            <cr-checkbox
                 checked="[[hangoutsLocalPresent_]]"
                 id="hangouts-local-present-checkbox"
                 on-change="onHangoutsLocalPresentChange_"
@@ -81,7 +82,7 @@
               <span id="hangouts-local-present-checkbox-subtitle">
                 [[i18n('hangoutsLocalPresentSubtitle')]]
               </span>
-            </paper-checkbox>
+            </cr-checkbox>
           </div>
         </div>
         <div id="mirroring-fullscreen-video-controls"
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
index 328aa1d..98f15e0 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
@@ -336,7 +336,7 @@
 
   /**
    * Called when the "smooth motion" box for Hangouts is changed by the user.
-   * @param {!{target: !PaperCheckboxElement}} e
+   * @param {!{target: !HTMLElement}} e
    * @private
    */
   onHangoutsLocalPresentChange_: function(e) {
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html b/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
index a2c53b9..3d55cbd8 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
@@ -61,11 +61,17 @@
       #pagelength {
         font-size: 0.81rem;
       }
+
+      iron-input input {
+        @apply --paper-input-container-shared-input-style;
+      }
     </style>
     <paper-input-container id="pageselector" no-label-float>
-      <input id="input" is="iron-input" value="{{pageNo}}" slot="input"
-          prevent-invalid-input allowed-pattern="\d" on-mouseup="select"
-          on-change="pageNoCommitted" aria-label$="{{strings.labelPageNumber}}">
+      <iron-input slot="input" bind-value="[[pageNo]]" allowed-pattern="\d">
+        <input id="input" prevent-invalid-input value="{{value::input}}"
+            on-mouseup="select" on-change="pageNoCommitted"
+            aria-label$="{{strings.labelPageNumber}}">
+      </iron-input>
     </paper-input-container>
     <span id="slash"> / </span>
     <span id="pagelength-spacer">
diff --git a/chrome/browser/resources/print_preview/new/destination_dialog.js b/chrome/browser/resources/print_preview/new/destination_dialog.js
index 64e24b2..8500c9f 100644
--- a/chrome/browser/resources/print_preview/new/destination_dialog.js
+++ b/chrome/browser/resources/print_preview/new/destination_dialog.js
@@ -237,9 +237,11 @@
         .then(
             response => {
               this.destinationInConfiguring_ = null;
-              destination.capabilities = response.capabilities;
-              listItem.onConfigureComplete(true);
-              this.selectDestination_(destination);
+              listItem.onConfigureComplete(response.success);
+              if (response.success) {
+                destination.capabilities = response.capabilities;
+                this.selectDestination_(destination);
+              }
             },
             () => {
               this.destinationInConfiguring_ = null;
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html
index 3b27059..4c4bc5a 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -44,7 +44,7 @@
             <input id="pageSettingsCustomInput" class="user-value" type="text"
                 checked="{{customSelected_::change}}" data-timeout-delay="500"
                 disabled$="[[getDisabled_(disabled, settings.pages.valid)]]"
-                pattern="([0-9]*(-)?[0-9]*(,)( )?)*([0-9]*(-)?[0-9]*(,)?( )?)?"
+                pattern="[[inputPattern_]]"
                 on-focus="onCustomInputFocus_" on-blur="onCustomInputBlur_"
                 placeholder="$i18n{examplePageRangeText}"
                 aria-label="$i18n{examplePageRangeText}">
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.js b/chrome/browser/resources/print_preview/new/pages_settings.js
index 616a365..741fa2d 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -68,6 +68,14 @@
       type: Array,
       computed: 'computeRangesToPrint_(pagesToPrint_, allPagesArray_)',
     },
+
+    /** @private {string} */
+    inputPattern_: {
+      type: String,
+      notify: true,
+      value:
+          '([0-9]*(-)?[0-9]*(,|\u3001)( )?)*([0-9]*(-)?[0-9]*(,|\u3001)?( )?)?',
+    },
   },
 
   observers: [
@@ -129,16 +137,18 @@
 
     const pages = [];
     const added = {};
-    const ranges = this.inputString_.split(',');
+    const ranges = this.inputString_.split(/,|\u3001/);
     const maxPage = this.allPagesArray_.length;
     for (let range of ranges) {
       range = range.trim();
-      if (range == '')
-        continue;
+      if (range == '') {
+        this.errorState_ = PagesInputErrorState.INVALID_SYNTAX;
+        return this.pagesToPrint_;
+      }
       const limits = range.split('-');
       let min = parseInt(limits[0], 10);
       if (min < 1) {
-        this.errorState_ = PagesInputErrorState.OUT_OF_BOUNDS;
+        this.errorState_ = PagesInputErrorState.INVALID_SYNTAX;
         return this.pagesToPrint_;
       }
       if (limits.length == 1) {
diff --git a/chrome/browser/resources/settings/controls/important_site_checkbox.html b/chrome/browser/resources/settings/controls/important_site_checkbox.html
index 20e0d91..982e4d6 100644
--- a/chrome/browser/resources/settings/controls/important_site_checkbox.html
+++ b/chrome/browser/resources/settings/controls/important_site_checkbox.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="settings_boolean_control_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
 
@@ -15,24 +15,16 @@
         width: 100%;
       }
 
-      paper-checkbox {
+      cr-checkbox {
         width: 100%;
       }
 
-      paper-checkbox:not([checked]) .secondary {
-        @apply --settings-secondary-unchecked;
-      }
-
       .middot {
         padding: 0 4px;
       }
-
-      .label {
-        @apply --settings-checkbox-label;
-      }
     </style>
     <div id="outerRow">
-      <paper-checkbox id="checkbox" checked="{{site.isChecked}}"
+      <cr-checkbox id="checkbox" checked="{{site.isChecked}}"
           disabled="[[disabled]]">
         <div class="label">[[site.registerableDomain]]</div>
         <div class="secondary label">
@@ -45,7 +37,7 @@
               $i18n{notificationWarning}
             </span>
         </div>
-      </paper-checkbox>
+      </cr-checkbox>
     </div>
   </template>
   <script src="important_site_checkbox.js"></script>
diff --git a/chrome/browser/resources/settings/controls/settings_checkbox.html b/chrome/browser/resources/settings/controls/settings_checkbox.html
index 5ea38bc..ec8f7cfe 100644
--- a/chrome/browser/resources/settings/controls/settings_checkbox.html
+++ b/chrome/browser/resources/settings/controls/settings_checkbox.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="settings_boolean_control_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
 
@@ -19,30 +19,19 @@
         min-height: var(--settings-row-min-height);
       }
 
-      paper-checkbox {
-        /* Handle overflow of subLabel when it is larger than one line. */
-        align-items: center;
-        display: flex;
+      cr-checkbox {
         /* Additional margin in case subLabel needs more than one line. */
         margin-bottom: 4px;
         margin-top: var(--settings-checkbox-margin-top, 4px);
         width: 100%;
       }
 
-      paper-checkbox:not([checked]) .secondary {
-        @apply --settings-secondary-unchecked;
-      }
-
       cr-policy-pref-indicator {
         -webkit-margin-start: var(--settings-controlled-by-spacing);
       }
-
-      .label {
-        @apply --settings-checkbox-label;
-      }
     </style>
     <div id="outerRow" noSubLabel$="[[!hasSubLabel_(subLabel, subLabelHtml)]]">
-      <paper-checkbox id="checkbox" checked="{{checked}}"
+      <cr-checkbox id="checkbox" checked="{{checked}}"
           on-change="notifyChangedByUserInteraction"
           disabled="[[controlDisabled(disabled, pref.*)]]">
         <div class="label">[[label]] <slot></slot></div>
@@ -50,7 +39,7 @@
           <div inner-h-t-m-l="[[subLabelHtml]]"></div>
           [[subLabel]]
         </div>
-      </paper-checkbox>
+      </cr-checkbox>
       <template is="dom-if" if="[[pref.controlledBy]]">
         <cr-policy-pref-indicator pref="[[pref]]" icon-aria-label="[[label]]">
         </cr-policy-pref-indicator>
diff --git a/chrome/browser/resources/settings/device_page/display.html b/chrome/browser/resources/settings/device_page/display.html
index acd9ad6a..ee1fa3a 100644
--- a/chrome/browser/resources/settings/device_page/display.html
+++ b/chrome/browser/resources/settings/device_page/display.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/html/assert.html">
@@ -82,11 +83,11 @@
     <template is="dom-if" if="[[showMirror_(unifiedDesktopMode_, displays)]]"
         restamp>
       <div class="secondary self-start">
-        <paper-checkbox checked="[[isMirrored_(displays)]]"
+        <cr-checkbox checked="[[isMirrored_(displays)]]"
             on-click="onMirroredTap_"
             aria-label="[[getDisplayMirrorText_(displays)]]">
           <div class="text-area">[[getDisplayMirrorText_(displays)]]</div>
-        </paper-checkbox>
+        </cr-checkbox>
       </div>
     </template>
 
diff --git a/chrome/browser/resources/settings/languages_page/BUILD.gn b/chrome/browser/resources/settings/languages_page/BUILD.gn
index b17b775..6784e76 100644
--- a/chrome/browser/resources/settings/languages_page/BUILD.gn
+++ b/chrome/browser/resources/settings/languages_page/BUILD.gn
@@ -57,7 +57,6 @@
     "..:lifetime_browser_proxy",
     "..:route",
     "../settings_page:settings_animated_pages",
-    "//third_party/polymer/v1_0/components-chromium/paper-checkbox:paper-checkbox-extracted",
     "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
     "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button",
     "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render",
@@ -80,7 +79,6 @@
     ":languages",
     ":languages_types",
     "../prefs:prefs",
-    "//third_party/polymer/v1_0/components-chromium/paper-checkbox:paper-checkbox-extracted",
   ]
   externs_list = [ "$externs_path/language_settings_private.js" ]
 }
@@ -89,7 +87,6 @@
   deps = [
     ":languages",
     ":languages_types",
-    "//third_party/polymer/v1_0/components-chromium/paper-checkbox:paper-checkbox-extracted",
     "//ui/webui/resources/cr_elements:cr_scrollable_behavior",
   ]
 }
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index 1e6b863..b6e5f6c 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -1,9 +1,9 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="languages.html">
 <link rel="import" href="../settings_page/settings_subpage_search.html">
@@ -33,10 +33,10 @@
         -webkit-padding-start: 20px;
       }
 
-      paper-checkbox {
-        --paper-checkbox-label: {
+      cr-checkbox {
+        --cr-checkbox-label-container: {
           white-space: nowrap;
-        }
+        };
       }
     </style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
@@ -49,12 +49,12 @@
             items="[[getLanguages_(
                 languages.supported, languages.enabled.*, filterValue_)]]">
           <template>
-            <paper-checkbox class="list-item no-outline"
+            <cr-checkbox class="list-item no-outline"
                 checked="[[willAdd_(item.code)]]" tabindex$="[[tabIndex]]"
                 title$="[[item.nativeDisplayName]]"
                 on-change="onLanguageCheckboxChange_">
               [[getDisplayText_(item)]]
-            </paper-checkbox>
+            </cr-checkbox>
           </template>
         </iron-list>
       </div>
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
index 30f4cfa2..81e3bf5 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
@@ -106,7 +106,7 @@
   /**
    * Handler for checking or unchecking a language item.
    * @param {!{model: !{item: !chrome.languageSettingsPrivate.Language},
-   *           target: !PaperCheckboxElement}} e
+   *           target: !Element}} e
    * @private
    */
   onLanguageCheckboxChange_: function(e) {
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 1b67fd0..b709740 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/action_link_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
@@ -9,7 +10,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html">
@@ -64,7 +64,8 @@
         margin: 6px 0 0 0;
       }
 
-      paper-checkbox.dropdown-item {
+      cr-checkbox.dropdown-item {
+        --cr-action-menu-disabled-item-opacity: 0.38;
         -webkit-margin-start: 0;
       }
 
@@ -318,7 +319,7 @@
             <cr-action-menu
                 class$="[[getMenuClass_(prefs.translate.enabled.value)]]">
 <if expr="chromeos or is_win">
-              <paper-checkbox id="uiLanguageItem" slot="item"
+              <cr-checkbox id="uiLanguageItem" slot="item"
                   class="dropdown-item"
                   checked="[[isProspectiveUILanguage_(
                       detailLanguage_.language.code,
@@ -326,14 +327,14 @@
                   on-change="onUILanguageChange_"
                   disabled="[[disableUILanguageCheckbox_(
                       detailLanguage_, languages.prospectiveUILanguage)]]">
-                      <span>$i18n{displayInThisLanguage}</span>
-                      <cr-policy-indicator indicator-type="[[
-                      getPolicyIndicatorStatus_(
-                        detailLanguage_.language)]]">
-                      </cr-policy-indicator>
-              </paper-checkbox>
+                <span>$i18n{displayInThisLanguage}</span>
+                <cr-policy-indicator indicator-type="[[
+                getPolicyIndicatorStatus_(
+                  detailLanguage_.language)]]">
+                </cr-policy-indicator>
+              </cr-checkbox>
 </if>
-              <paper-checkbox id="offerTranslations" slot="item"
+              <cr-checkbox id="offerTranslations" slot="item"
                   class="dropdown-item"
                   checked="[[detailLanguage_.translateEnabled]]"
                   on-change="onTranslateCheckboxChange_"
@@ -341,7 +342,7 @@
                   disabled="[[disableTranslateCheckbox_(
                       detailLanguage_.language, languages.translateTarget)]]">
                 $i18n{offerToTranslateInThisLanguage}
-              </paper-checkbox>
+              </cr-checkbox>
               <hr slot="item">
               <button slot="item" class="dropdown-item" role="menuitem"
                   on-click="onMoveToTopTap_"
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.js b/chrome/browser/resources/settings/languages_page/languages_page.js
index 7dbbaad..3a90fdc20 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -323,7 +323,7 @@
 
   /**
    * Handler for changes to the UI language checkbox.
-   * @param {!{target: !PaperCheckboxElement}} e
+   * @param {!{target: !Element}} e
    * @private
    */
   onUILanguageChange_: function(e) {
@@ -353,7 +353,7 @@
 
   /**
    * Handler for changes to the translate checkbox.
-   * @param {!{target: !PaperCheckboxElement}} e
+   * @param {!{target: !Element}} e
    * @private
    */
   onTranslateCheckboxChange_: function(e) {
diff --git a/chrome/browser/resources/settings/languages_page/manage_input_methods_page.html b/chrome/browser/resources/settings/languages_page/manage_input_methods_page.html
index e877d37..520e612b 100644
--- a/chrome/browser/resources/settings/languages_page/manage_input_methods_page.html
+++ b/chrome/browser/resources/settings/languages_page/manage_input_methods_page.html
@@ -1,8 +1,8 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="languages.html">
 
@@ -16,12 +16,12 @@
           <template is="dom-repeat" items="[[item.inputMethods]]">
             <div class="list-item">
               <div class="language-name">
-                <paper-checkbox checked="[[item.enabled]]"
+                <cr-checkbox checked="[[item.enabled]]"
                     on-change="onCheckboxChange_"
                     disabled="[[!enableInputMethodCheckbox_(
                         item, languages.inputMethods.enabled.*)]]">
                   <span>[[item.displayName]]</span>
-                </paper-checkbox>
+                </cr-checkbox>
               </div>
             </div>
           </template>
diff --git a/chrome/browser/resources/settings/languages_page/manage_input_methods_page.js b/chrome/browser/resources/settings/languages_page/manage_input_methods_page.js
index bef3106..2c3ede7 100644
--- a/chrome/browser/resources/settings/languages_page/manage_input_methods_page.js
+++ b/chrome/browser/resources/settings/languages_page/manage_input_methods_page.js
@@ -57,7 +57,7 @@
   /**
    * Handler for an input method checkbox.
    * @param {!{model: !{item: chrome.languageSettingsPrivate.InputMethod},
-   *           target: !PaperCheckboxElement}} e
+   *           target: !Element}} e
    * @private
    */
   onCheckboxChange_: function(e) {
diff --git a/chrome/browser/resources/settings/people_page/password_prompt_dialog.js b/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
index 34577fe..437d94d4 100644
--- a/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
+++ b/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
@@ -118,7 +118,7 @@
         // Select the whole password if user entered an incorrect password.
         // Return focus to the password input if it lost focus while being
         // checked (user pressed confirm button).
-        this.$.passwordInput.inputElement.select();
+        this.$.passwordInput.inputElement.inputElement.select();
         if (!this.$.passwordInput.focused)
           this.$.passwordInput.focus();
         return;
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index d826f5e..7b84491 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
@@ -13,7 +14,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
 <link rel="import" href="sync_page.html">
@@ -407,10 +407,10 @@
         <template is="dom-if" if="[[!syncStatus.domain]]">
           <div id="wideFooter" slot="footer">
             <div class="settings-box first">
-              <paper-checkbox id="deleteProfile" class="start"
+              <cr-checkbox id="deleteProfile" class="start"
                   checked="{{deleteProfile_}}">
                 $i18n{syncDisconnectDeleteProfile}
-              </paper-checkbox>
+              </cr-checkbox>
               <cr-expand-button expanded="{{deleteProfileWarningVisible_}}"
                   alt="$i18n{deleteProfileWarningExpandA11yLabel}">
               </cr-expand-button>
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index 7fe57e01..d17d5cd 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -265,7 +265,7 @@
       listenOnce(document, 'show-container', () => {
         const input = /** @type {!PaperInputElement} */ (
             this.$$('#existingPassphraseInput'));
-        input.inputElement.focus();
+        input.focus();
       });
     }
   },
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
index 70ae6da0..0b7ecc7 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
@@ -59,11 +59,16 @@
       #dropdownIcon {
         background-size: 24px;
       }
+
+      iron-input input {
+        @apply --paper-input-container-shared-input-style;
+      }
     </style>
     <paper-input-container no-label-float on-click="onTap_">
-      <input is="iron-input" type="search" bind-value="{{selectedItem}}"
-          on-search="onInputValueChanged_" on-change="onChange_" incremental
-          slot="input">
+      <iron-input id="search" slot="input" bind-value="{{selectedItem}}">
+        <input type="search" on-search="onInputValueChanged_"
+            value="{{value::input}}" on-change="onChange_" incremental>
+      </iron-input>
       <paper-icon-button-light id="searchIcon" class="icon-search" hidden
           slot="suffix">
         <button></button>
diff --git a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
index 026ed96e..451ef9faf 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
@@ -97,11 +97,9 @@
         outline: none;
       }
 
-      paper-input-container {
-        --paper-input-container-input: {
-          font: inherit;
-        };
-        --paper-input-container-input-color: inherit;
+      #search input[type='search'] {
+        @apply --paper-input-container-shared-input-style;
+        font: inherit;
       }
     </style>
   </template>
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.js b/chrome/browser/resources/settings/printing_page/cups_printers.js
index b0ee57c8..593a477b 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.js
@@ -59,6 +59,8 @@
   /** @override */
   attached: function() {
     this.addWebUIListener('on-add-cups-printer', this.onAddPrinter_.bind(this));
+    this.addWebUIListener(
+        'on-printers-changed', this.printersChanged_.bind(this));
     this.networksChangedListener_ = this.refreshNetworks_.bind(this);
     chrome.networkingPrivate.onNetworksChanged.addListener(
         this.networksChangedListener_);
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js
index 1e2080b..1ba1e5b6 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js
@@ -78,7 +78,7 @@
 
   /** @override */
   attached: function() {
-    this.updateActionButtonState_();
+    this.async(this.updateActionButtonState_.bind(this));
     this.browserProxy_.searchEngineEditStarted(
         this.model ? this.model.modelIndex : this.DEFAULT_MODEL_INDEX);
     this.$.dialog.showModal();
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index c0d1c124..3117c29 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -1,5 +1,4 @@
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_input_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/search_highlight_style_css.html">
@@ -12,7 +11,7 @@
 <!-- Common styles for Material Design settings. -->
 <dom-module id="settings-shared">
   <template>
-    <style include="settings-icons paper-button-style paper-checkbox-style paper-input-style paper-toggle-style cr-shared-style search-highlight-style">
+    <style include="settings-icons paper-button-style paper-input-style paper-toggle-style cr-shared-style search-highlight-style">
       /* Prevent action-links from being selected to avoid accidental
        * selection when trying to click it. */
       a[is=action-link] {
diff --git a/chrome/browser/resources/settings/site_settings/add_site_dialog.html b/chrome/browser/resources/settings/site_settings/add_site_dialog.html
index 9d78be71..87c2439 100644
--- a/chrome/browser/resources/settings/site_settings/add_site_dialog.html
+++ b/chrome/browser/resources/settings/site_settings/add_site_dialog.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -29,10 +30,10 @@
             value="{{site_}}" on-input="validate_"
             error-message="$i18n{notValidWebAddress}" spellcheck="false"
             autofocus></paper-input>
-        <paper-checkbox id="incognito"
+        <cr-checkbox id="incognito"
             invisible$="[[!showIncognitoSessionOnly_]]">
           $i18n{incognitoSiteOnly}
-        </paper-checkbox>
+        </cr-checkbox>
       </div>
       <div slot="button-container">
         <paper-button class="cancel-button" on-click="onCancelTap_">
diff --git a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
index 654d24642..be6faee4 100644
--- a/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
+++ b/chrome/browser/resources/settings/site_settings/edit_exception_dialog.js
@@ -13,7 +13,10 @@
     /**
      * @type {!SiteException}
      */
-    model: Object,
+    model: {
+      type: Object,
+      observer: 'modelChanged_',
+    },
 
     /** @private */
     origin_: String,
@@ -72,4 +75,10 @@
       this.invalid_ = !isValid;
     });
   },
+
+  /** @private */
+  modelChanged_: function() {
+    if (!this.model)
+      this.$.dialog.cancel();
+  },
 });
diff --git a/chrome/browser/resources/settings/site_settings/site_list.js b/chrome/browser/resources/settings/site_settings/site_list.js
index 650f86c..29c4646 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.js
+++ b/chrome/browser/resources/settings/site_settings/site_list.js
@@ -370,8 +370,10 @@
   onEditExceptionDialogClosed_: function() {
     this.showEditExceptionDialog_ = false;
     this.actionMenuSite_ = null;
-    this.activeDialogAnchor_.focus();
-    this.activeDialogAnchor_ = null;
+    if (this.activeDialogAnchor_) {
+      this.activeDialogAnchor_.focus();
+      this.activeDialogAnchor_ = null;
+    }
   },
 
   /** @private */
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index adffd16..c7a5198e 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -240,6 +240,11 @@
   return network_context_->GetURLLoaderFactory();
 }
 
+void SafeBrowsingService::FlushNetworkInterfaceForTesting() {
+  if (network_context_)
+    network_context_->FlushForTesting();
+}
+
 scoped_refptr<network::SharedURLLoaderFactory>
 SafeBrowsingService::GetURLLoaderFactoryOnIOThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index a2c1b8a..481e357 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -156,6 +156,9 @@
   network::mojom::NetworkContext* GetNetworkContext();
   virtual scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
 
+  // Flushes above two interfaces to avoid races in tests.
+  void FlushNetworkInterfaceForTesting();
+
   // Called to get a SharedURLLoaderFactory that can be used on the IO thread.
   scoped_refptr<network::SharedURLLoaderFactory>
   GetURLLoaderFactoryOnIOThread();
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index f1294054..2ba92d4 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -1826,8 +1826,8 @@
  public:
   void SetUp() override {
     JsRequestTestParam param = GetParam();
-    if (param.request_type == JsRequestType::kOffMainThreadWebSocket) {
-      scoped_feature_list_.InitAndEnableFeature(
+    if (param.request_type == JsRequestType::kWebSocket) {
+      scoped_feature_list_.InitAndDisableFeature(
           features::kOffMainThreadWebSocket);
     }
     SafeBrowsingServiceTest::SetUp();
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
index 8f39d80..21f860e 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc
@@ -97,7 +97,7 @@
     "  }"
     "}";
 
-class SettingsResetPromptModelBrowserTest : public ExtensionBrowserTest {
+class SettingsResetPromptModelBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   virtual void OnResetDone() { ++reset_callbacks_; }
 
@@ -108,7 +108,7 @@
       : startup_pref_(SessionStartupPref::URLS) {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
     // Set up an active homepage with visible homepage button.
     PrefService* prefs = profile()->GetPrefs();
diff --git a/chrome/browser/search_engines/template_url_android.cc b/chrome/browser/search_engines/template_url_android.cc
new file mode 100644
index 0000000..9acf79d9c
--- /dev/null
+++ b/chrome/browser/search_engines/template_url_android.cc
@@ -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.
+
+#include "chrome/browser/search_engines/template_url_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "components/search_engines/template_url.h"
+
+#include "jni/TemplateUrl_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+TemplateURL* ToTemplateURL(jlong j_template_url) {
+  return reinterpret_cast<TemplateURL*>(j_template_url);
+}
+
+ScopedJavaLocalRef<jstring> JNI_TemplateUrl_GetShortName(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jcaller,
+    jlong template_url_ptr) {
+  TemplateURL* template_url = ToTemplateURL(template_url_ptr);
+  return base::android::ConvertUTF16ToJavaString(env,
+                                                 template_url->short_name());
+}
+
+ScopedJavaLocalRef<jstring> JNI_TemplateUrl_GetKeyword(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jcaller,
+    jlong template_url_ptr) {
+  TemplateURL* template_url = ToTemplateURL(template_url_ptr);
+  return base::android::ConvertUTF16ToJavaString(env, template_url->keyword());
+}
+
+jboolean JNI_TemplateUrl_IsPrepopulatedOrCreatedByPolicy(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jcaller,
+    jlong template_url_ptr) {
+  TemplateURL* template_url = ToTemplateURL(template_url_ptr);
+  return template_url->prepopulate_id() > 0 ||
+         template_url->created_by_policy();
+}
+
+jlong JNI_TemplateUrl_GetLastVisitedTime(JNIEnv* env,
+                                         const JavaParamRef<jclass>& jcaller,
+                                         jlong template_url_ptr) {
+  TemplateURL* template_url = ToTemplateURL(template_url_ptr);
+  return template_url->last_visited().ToJavaTime();
+}
+
+jint JNI_TemplateUrl_GetPrepopulatedId(JNIEnv* env,
+                                       const JavaParamRef<jclass>& jcaller,
+                                       jlong template_url_ptr) {
+  TemplateURL* template_url = ToTemplateURL(template_url_ptr);
+  return template_url->prepopulate_id();
+}
+
+ScopedJavaLocalRef<jobject> CreateTemplateUrlAndroid(
+    JNIEnv* env,
+    const TemplateURL* template_url) {
+  return Java_TemplateUrl_create(env, reinterpret_cast<intptr_t>(template_url));
+}
diff --git a/chrome/browser/search_engines/template_url_android.h b/chrome/browser/search_engines/template_url_android.h
new file mode 100644
index 0000000..61bf2e8
--- /dev/null
+++ b/chrome/browser/search_engines/template_url_android.h
@@ -0,0 +1,20 @@
+// 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_SEARCH_ENGINES_TEMPLATE_URL_ANDROID_H_
+#define CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/search_engines/template_url.h"
+
+// Android wrapper of the TemplateUrl which provides access for the Java
+// layer.
+class TemplateUrlAndroid;
+
+base::android::ScopedJavaLocalRef<jobject> CreateTemplateUrlAndroid(
+    JNIEnv* env,
+    const TemplateURL* template_url);
+
+#endif  // CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_ANDROID_H_
diff --git a/chrome/browser/search_engines/template_url_service_android.cc b/chrome/browser/search_engines/template_url_service_android.cc
index 927aa93..3800024 100644
--- a/chrome/browser/search_engines/template_url_service_android.cc
+++ b/chrome/browser/search_engines/template_url_service_android.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -14,6 +15,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/search_engines/template_url_android.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/search_engines/search_terms_data.h"
@@ -47,8 +49,6 @@
           base::Bind(&TemplateUrlServiceAndroid::OnTemplateURLServiceLoaded,
                      base::Unretained(this)));
   template_url_service_->AddObserver(this);
-  if (template_url_service_->loaded() && template_urls_.empty())
-    LoadTemplateURLs();
 }
 
 TemplateUrlServiceAndroid::~TemplateUrlServiceAndroid() {
@@ -71,30 +71,12 @@
   template_url_service_->SetUserSelectedDefaultSearchProvider(template_url);
 }
 
-jint TemplateUrlServiceAndroid::GetDefaultSearchProviderIndex(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) const {
-  const TemplateURL* default_search_provider =
-      template_url_service_->GetDefaultSearchProvider();
-  auto it = std::find(template_urls_.begin(), template_urls_.end(),
-                      default_search_provider);
-  size_t default_search_provider_index_ =
-      (it == template_urls_.end()) ? -1 : (it - template_urls_.begin());
-  return default_search_provider_index_;
-}
-
 jboolean TemplateUrlServiceAndroid::IsLoaded(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) const {
   return template_url_service_->loaded();
 }
 
-jint TemplateUrlServiceAndroid::GetTemplateUrlCount(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) const {
-  return template_urls_.size();
-}
-
 jboolean TemplateUrlServiceAndroid::IsDefaultSearchManaged(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
@@ -159,81 +141,21 @@
       url);
 }
 
-base::android::ScopedJavaLocalRef<jobject>
-TemplateUrlServiceAndroid::GetTemplateUrlAt(JNIEnv* env,
-                                            const JavaParamRef<jobject>& obj,
-                                            jint index) const {
-  TemplateURL* template_url = template_urls_[index];
-  return Java_TemplateUrl_create(
-      env, index,
-      base::android::ConvertUTF16ToJavaString(env, template_url->short_name()),
-      template_url_service_->IsPrepopulatedOrCreatedByPolicy(template_url),
-      base::android::ConvertUTF16ToJavaString(env, template_url->keyword()));
-}
-
 void TemplateUrlServiceAndroid::OnTemplateURLServiceLoaded() {
   template_url_subscription_.reset();
   JNIEnv* env = base::android::AttachCurrentThread();
   auto java_obj = weak_java_obj_.get(env);
   if (java_obj.is_null())
     return;
-  LoadTemplateURLs();
 
   Java_TemplateUrlService_templateUrlServiceLoaded(env, java_obj);
 }
 
-void TemplateUrlServiceAndroid::LoadTemplateURLs() {
-  template_urls_ = template_url_service_->GetTemplateURLs();
-
-  // Move prepopulated and policy-created engines to the front of list,
-  // and sort by prepopulated_id.
-  TemplateURLService* template_url_service = template_url_service_;
-  auto it = std::partition(
-      template_urls_.begin(), template_urls_.end(),
-      [template_url_service](const TemplateURL* t_url) {
-        return template_url_service->IsPrepopulatedOrCreatedByPolicy(t_url);
-      });
-  std::sort(template_urls_.begin(), it,
-            [](const TemplateURL* lhs, const TemplateURL* rhs) {
-              return lhs->prepopulate_id() < rhs->prepopulate_id();
-            });
-
-  // Place any user-selected default engine next.
-  const TemplateURL* dsp = template_url_service_->GetDefaultSearchProvider();
-  it = std::partition(it, template_urls_.end(),
-                      [dsp](const TemplateURL* t_url) { return t_url == dsp; });
-
-  // Sort the remaining engines to place the three most recently-visited first.
-  constexpr size_t kMaxRecentUrls = 3;
-  const size_t recent_url_num = template_urls_.end() - it;
-  int urls_to_show = filtering_enabled_
-                         ? std::min(recent_url_num, kMaxRecentUrls)
-                         : recent_url_num;
-  auto end = it + urls_to_show;
-
-  std::partial_sort(it, end, template_urls_.end(),
-                    [](const TemplateURL* lhs, const TemplateURL* rhs) {
-                      return lhs->last_visited() > rhs->last_visited();
-                    });
-
-  if (filtering_enabled_) {
-    // Limit to those three engines which must also have been visited in the
-    // last two days.
-    constexpr base::TimeDelta kMaxVisitAge = base::TimeDelta::FromDays(2);
-    const base::Time cutoff = base::Time::Now() - kMaxVisitAge;
-    const auto too_old = [cutoff](const TemplateURL* t_url) {
-      return t_url->last_visited() < cutoff;
-    };
-    template_urls_.erase(std::find_if(it, end, too_old), template_urls_.end());
-  }
-}
-
 void TemplateUrlServiceAndroid::OnTemplateURLServiceChanged() {
   JNIEnv* env = base::android::AttachCurrentThread();
   auto java_obj = weak_java_obj_.get(env);
   if (java_obj.is_null())
     return;
-  LoadTemplateURLs();
 
   Java_TemplateUrlService_onTemplateURLServiceChanged(env, java_obj);
 }
@@ -356,17 +278,6 @@
   return base::android::ConvertUTF8ToJavaString(env, url);
 }
 
-void TemplateUrlServiceAndroid::SetFilteringEnabled(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj,
-    jboolean filtering_enabled) {
-  if (filtering_enabled == filtering_enabled_)
-    return;
-
-  filtering_enabled_ = filtering_enabled;
-  OnTemplateURLServiceChanged();
-}
-
 base::android::ScopedJavaLocalRef<jstring>
 TemplateUrlServiceAndroid::AddSearchEngineForTesting(
     JNIEnv* env,
@@ -422,6 +333,32 @@
       env, (has_search_terms ? search_terms : base::string16()));
 }
 
+void TemplateUrlServiceAndroid::GetTemplateUrls(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jobject>& template_url_list_obj) {
+  std::vector<TemplateURL*> template_urls =
+      template_url_service_->GetTemplateURLs();
+  for (TemplateURL* template_url : template_urls) {
+    base::android::ScopedJavaLocalRef<jobject> j_template_url =
+        CreateTemplateUrlAndroid(env, template_url);
+    Java_TemplateUrlService_addTemplateUrlToList(env, template_url_list_obj,
+                                                 j_template_url);
+  };
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+TemplateUrlServiceAndroid::GetDefaultSearchEngine(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  const TemplateURL* default_search_provider =
+      template_url_service_->GetDefaultSearchProvider();
+  if (default_search_provider == nullptr) {
+    return base::android::ScopedJavaLocalRef<jobject>(env, nullptr);
+  }
+  return CreateTemplateUrlAndroid(env, default_search_provider);
+}
+
 static jlong JNI_TemplateUrlService_Init(JNIEnv* env,
                                          const JavaParamRef<jobject>& obj) {
   TemplateUrlServiceAndroid* template_url_service_android =
diff --git a/chrome/browser/search_engines/template_url_service_android.h b/chrome/browser/search_engines/template_url_service_android.h
index 7cd432fa..e539abc 100644
--- a/chrome/browser/search_engines/template_url_service_android.h
+++ b/chrome/browser/search_engines/template_url_service_android.h
@@ -28,21 +28,9 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& jkeyword);
-
-  jint GetDefaultSearchProviderIndex(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj) const;
-
-  jint GetTemplateUrlCount(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj) const;
   jboolean IsLoaded(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj) const;
-  base::android::ScopedJavaLocalRef<jobject> GetTemplateUrlAt(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      jint index) const;
   jboolean IsDefaultSearchManaged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
@@ -83,9 +71,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& jkeyword);
-  void SetFilteringEnabled(JNIEnv* env,
-                           const base::android::JavaParamRef<jobject>& obj,
-                           jboolean filtering_enabled);
   base::android::ScopedJavaLocalRef<jstring> ExtractSearchTermsFromUrl(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
@@ -106,6 +91,18 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& jkeyword);
 
+  // Get all the available search engines and add them to the
+  // |template_url_list_obj| list.
+  void GetTemplateUrls(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& template_url_list_obj);
+
+  // Get current default search engine.
+  base::android::ScopedJavaLocalRef<jobject> GetDefaultSearchEngine(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
  private:
   ~TemplateUrlServiceAndroid() override;
 
@@ -114,12 +111,6 @@
   // TemplateUrlServiceObserver:
   void OnTemplateURLServiceChanged() override;
 
-  // Updates |template_urls_| to contain all TemplateURLs.  It sorts this list
-  // with prepopulated engines first, then any default non-prepopulated engine,
-  // then other non-prepopulated engines based on last_visited in descending
-  // order.
-  void LoadTemplateURLs();
-
   JavaObjectWeakGlobalRef weak_java_obj_;
 
   // Pointer to the TemplateUrlService for the main profile.
@@ -127,11 +118,6 @@
 
   std::unique_ptr<TemplateURLService::Subscription> template_url_subscription_;
 
-  // Caches the up-to-date TemplateURL list so that calls from Android could
-  // directly get data from it.
-  std::vector<TemplateURL*> template_urls_;
-  bool filtering_enabled_ = true;
-
   DISALLOW_COPY_AND_ASSIGN(TemplateUrlServiceAndroid);
 };
 
diff --git a/chrome/browser/site_details_browsertest.cc b/chrome/browser/site_details_browsertest.cc
index ca793026..4ec99e0 100644
--- a/chrome/browser/site_details_browsertest.cc
+++ b/chrome/browser/site_details_browsertest.cc
@@ -166,13 +166,13 @@
 
 }  // namespace
 
-class SiteDetailsBrowserTest : public ExtensionBrowserTest {
+class SiteDetailsBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   SiteDetailsBrowserTest() {}
   ~SiteDetailsBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
 
     // Add content/test/data so we can use cross_site_iframe_factory.html
diff --git a/chrome/browser/ssl/certificate_transparency_browsertest.cc b/chrome/browser/ssl/certificate_transparency_browsertest.cc
deleted file mode 100644
index 190b3ea..0000000
--- a/chrome/browser/ssl/certificate_transparency_browsertest.cc
+++ /dev/null
@@ -1,284 +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 "base/base64.h"
-#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/net/sth_distributor_provider.h"
-#include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ssl/cert_verifier_browser_test.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/certificate_transparency/features.h"
-#include "components/certificate_transparency/single_tree_tracker.h"
-#include "components/certificate_transparency/sth_distributor.h"
-#include "components/certificate_transparency/tree_state_tracker.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/cert/ct_policy_status.h"
-#include "net/cert/ct_serialization.h"
-#include "net/cert/mock_cert_verifier.h"
-#include "net/cert/signed_tree_head.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/cert_test_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_data_directory.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace {
-
-using net::ct::DigitallySigned;
-using net::ct::SignedTreeHead;
-using net::test::IsOk;
-
-constexpr base::TimeDelta kZeroTTL;
-
-// Decodes a base64-encoded "DigitallySigned" TLS struct into |*sig_out|.
-// See https://tools.ietf.org/html/rfc5246#section-4.7.
-// |sig_out| must not be null.
-bool DecodeDigitallySigned(base::StringPiece base64_data,
-                           DigitallySigned* sig_out) {
-  std::string data;
-  if (!base::Base64Decode(base64_data, &data))
-    return false;
-
-  base::StringPiece data_ptr = data;
-  if (!net::ct::DecodeDigitallySigned(&data_ptr, sig_out))
-    return false;
-
-  return true;
-}
-
-// Populates |*sth_out| with the given information.
-// |sth_out| must not be null.
-bool BuildSignedTreeHead(base::Time timestamp,
-                         uint64_t tree_size,
-                         base::StringPiece root_hash_base64,
-                         base::StringPiece signature_base64,
-                         base::StringPiece log_id_base64,
-                         net::ct::SignedTreeHead* sth_out) {
-  sth_out->version = SignedTreeHead::V1;
-  sth_out->timestamp = timestamp;
-  sth_out->tree_size = tree_size;
-
-  std::string root_hash;
-  if (!base::Base64Decode(root_hash_base64, &root_hash)) {
-    return false;
-  }
-  root_hash.copy(sth_out->sha256_root_hash, net::ct::kSthRootHashLength);
-
-  return DecodeDigitallySigned(signature_base64, &sth_out->signature) &&
-         base::Base64Decode(log_id_base64, &sth_out->log_id);
-}
-
-// Runs |closure| on the IO thread and waits until it completes.
-void RunOnIOThreadBlocking(base::OnceClosure closure) {
-  base::RunLoop run_loop;
-
-  content::BrowserThread::PostTaskAndReply(content::BrowserThread::IO,
-                                           FROM_HERE, std::move(closure),
-                                           run_loop.QuitClosure());
-
-  run_loop.Run();
-}
-
-// Adds an entry to the HostCache used by a particular URLRequestContext.
-// The entry will map |hostname| to |ip_literal|, as though a DNS lookup had
-// been performed and an an A/AAAA record had been found for this mapping.
-void AddCacheEntryOnIOThread(net::URLRequestContextGetter* context_getter,
-                             const std::string& hostname,
-                             const std::string& ip_literal) {
-  ASSERT_TRUE(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  net::HostCache* cache =
-      context_getter->GetURLRequestContext()->host_resolver()->GetHostCache();
-  ASSERT_TRUE(cache);
-
-  net::AddressList address_list;
-  ASSERT_THAT(net::ParseAddressList(ip_literal, hostname, &address_list),
-              IsOk());
-
-  cache->Set(net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
-             net::HostCache::Entry(net::OK, address_list,
-                                   net::HostCache::Entry::SOURCE_DNS),
-             base::TimeTicks::Now(), kZeroTTL);
-}
-
-// A test fixture that can enable Certificate Transparency checks, then initiate
-// a connection to a test HTTPS server and intercept certificate verification so
-// that those checks can be performed successfully. This involves substituting a
-// test certificate for a real certificate containing Signed Certificate
-// Timestamps. Certificate verification is hard-coded to succeed, so the real
-// certificate will never be treated as expired.
-class CertificateTransparencyBrowserTest : public CertVerifierBrowserTest {
- public:
-  CertificateTransparencyBrowserTest()
-      : CertVerifierBrowserTest(),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    feature_list_.InitWithFeatures({certificate_transparency::kCTLogAuditing},
-                                   {});
-  }
-
- protected:
-  void TestCTHistogramsArePopulated(bool use_profile) {
-    net::URLRequestContextGetter* request_context = nullptr;
-    if (use_profile) {
-      request_context = browser()->profile()->GetRequestContext();
-    } else {
-      request_context = g_browser_process->system_request_context();
-    }
-
-    // Provide an STH from Google's Pilot log that can be used to prove
-    // inclusion for an SCT later in the test.
-    SignedTreeHead pilot_sth;
-    ASSERT_TRUE(BuildSignedTreeHead(
-        base::Time::FromJsTime(1512419914170), 181871752,
-        "bvgljSy3Yg32Y6J8qL5WmUA3jn2WnOrEFDqxD0AxUvs=",
-        "BAMARjBEAiAwEXve2RBk3XkUR+6nACSETTgzKFaEeginxuj5U9BI/"
-        "wIgBPuQS5ACxsro6TtpY4bQyE6WlMdcSMiMd/SSGraOBOg=",
-        "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", &pilot_sth));
-    chrome_browser_net::GetGlobalSTHDistributor()->NewSTHObserved(pilot_sth);
-
-    // Provide an STH from Google's Aviator log that is not recent enough to
-    // prove inclusion for an SCT later in the test.
-    SignedTreeHead aviator_sth;
-    ASSERT_TRUE(BuildSignedTreeHead(
-        base::Time::FromJsTime(1442652106945), 8502329,
-        "bfG+gWZcHl9fqtNo0Z/uggs8E5YqGOtJQ0Z5zVZDRxI=",
-        "BAMARjBEAiA6elcNQoShmKLHj/"
-        "IA649UIbaQtWJEpj0Eot0q7G6fEgIgYChb7U6Reuvt0nO5PionH+3UciOxKV3Cy8/"
-        "eq59lSYY=",
-        "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=", &aviator_sth));
-    chrome_browser_net::GetGlobalSTHDistributor()->NewSTHObserved(aviator_sth);
-
-    // TODO(robpercival): Override CT log list to ensure it includes Google's
-    // Pilot and Aviator CT logs, as well as DigiCert's CT log.
-
-    // Connect to https_server_ using the name "localhost", rather than by IP
-    // address. CT inclusion checks are skipped if a host was not looked up
-    // using DNS, so a hostname must be used.
-    https_server_.SetSSLConfig(
-        net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
-    ASSERT_TRUE(https_server_.Start());
-
-    // Add "localhost" to the HostCache, so that it appears that a DNS lookup
-    // was performed for this hostname. This will make the SCTs provided by the
-    // server eligible for inclusion checks.
-    RunOnIOThreadBlocking(base::BindOnce(&AddCacheEntryOnIOThread,
-                                         base::RetainedRef(request_context),
-                                         "localhost", "127.0.0.1"));
-
-    // Swap the normal test cert for one that contains 3 SCTs and fulfills
-    // Chrome's CT policy. To do so, setup the mock_cert_verifier to swap the
-    // HTTP server's certificate for a different one during the verification
-    // process, and return a CertVerifyResult that indicates that this
-    // replacement cert is valid and issued by a known root (CT checks are
-    // skipped for private roots).
-    net::CertVerifyResult verify_result;
-    verify_result.is_issued_by_known_root = true;
-    verify_result.cert_status = 0;
-
-    {
-      base::ScopedAllowBlockingForTesting allow_blocking_for_loading_cert;
-
-      verify_result.verified_cert = net::CreateCertificateChainFromFile(
-          net::GetTestCertsDirectory(), "comodo-chain.pem",
-          net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
-      ASSERT_TRUE(verify_result.verified_cert);
-    }
-
-    mock_cert_verifier()->AddResultForCert(https_server_.GetCertificate(),
-                                           verify_result, net::OK);
-
-    const int kNumSCTs = 3;  // Number of SCTs in verified_cert
-    base::HistogramTester histograms;
-
-    // Navigate to a test server URL, which should trigger a CT inclusion check
-    // thanks to the cert mock_cert_verifier swaps in.
-    GURL url = https_server_.GetURL("/");
-    if (use_profile) {
-      ui_test_utils::NavigateToURL(browser(), url);
-    } else {
-      content::LoadBasicRequest(
-          g_browser_process->system_network_context_manager()->GetContext(),
-          url);
-    }
-
-    // Find out how many connections Chrome made (it may make more than one).
-    const base::HistogramBase::Count connection_count =
-        histograms.GetBucketCount("Net.SSL_Connection_Error", net::OK);
-    ASSERT_TRUE(connection_count > 0);
-
-    // Expect 3 SCTs in each connection.
-    EXPECT_THAT(histograms.GetBucketCount(
-                    "Net.CertificateTransparency.SCTsPerConnection", kNumSCTs),
-                connection_count);
-
-    // Expect that the SCTs were embedded in the certificate.
-    EXPECT_THAT(histograms.GetBucketCount(
-                    "Net.CertificateTransparency.SCTOrigin",
-                    static_cast<int>(
-                        net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)),
-                kNumSCTs * connection_count);
-
-    // The certificate contains a sufficient number and diversity of SCTs.
-    // 2 from Google CT logs (Pilot, Aviator) and 1 from a non-Google CT log
-    // (DigiCert).
-    histograms.ExpectUniqueSample(
-        "Net.CertificateTransparency.ConnectionComplianceStatus2.SSL",
-        static_cast<int>(
-            net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS),
-        connection_count);
-
-    // The CanInclusionCheckSCT histogram should only have a single sample in
-    // each bucket, because SCTs should be de-duplicated prior to inclusion
-    // checks.
-
-    // The Pilot SCT should be eligible for inclusion checking, because a recent
-    // enough Pilot STH is available.
-    histograms.ExpectBucketCount(
-        "Net.CertificateTransparency.CanInclusionCheckSCT",
-        certificate_transparency::CAN_BE_CHECKED, 1);
-    // The Aviator SCT should not be eligible for inclusion checking, because
-    // there is not a recent enough Aviator STH available.
-    histograms.ExpectBucketCount(
-        "Net.CertificateTransparency.CanInclusionCheckSCT",
-        certificate_transparency::NEWER_STH_REQUIRED, 1);
-    // The DigiCert SCT should not be eligible for inclusion checking, because
-    // there is no DigiCert STH available.
-    histograms.ExpectBucketCount(
-        "Net.CertificateTransparency.CanInclusionCheckSCT",
-        certificate_transparency::VALID_STH_REQUIRED, 1);
-  }
-
-  net::EmbeddedTestServer https_server_;
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(CertificateTransparencyBrowserTest);
-};
-
-// Tests that wiring is correctly setup in ProfileIOData to pass Signed
-// Certificate Timestamps (SCTs) through from the SSL socket code to the CT
-// verification and inclusion checking code. This is assessed by checking that
-// histograms are correctly populated.
-IN_PROC_BROWSER_TEST_F(CertificateTransparencyBrowserTest, ProfileRequest) {
-  ASSERT_NO_FATAL_FAILURE(TestCTHistogramsArePopulated(true));
-}
-
-// Tests that wiring is correctly setup in IOThread to pass Signed Certificate
-// Timestamps (SCTs) through from the SSL socket code to the CT verification and
-// inclusion checking code. This is assessed by checking that histograms are
-// correctly populated.
-IN_PROC_BROWSER_TEST_F(CertificateTransparencyBrowserTest, SystemRequest) {
-  ASSERT_NO_FATAL_FAILURE(TestCTHistogramsArePopulated(false));
-}
-
-}  // namespace
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
index a7a108d..47983c71 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -17,6 +17,7 @@
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
@@ -38,6 +39,9 @@
 
 namespace {
 
+const char kRecurrentInterstitialThresholdParam[] = "threshold";
+const int kRecurrentInterstitialDefaultThreshold = 3;
+
 // The default expiration is one week, unless overidden by a field trial group.
 // See https://crbug.com/487270.
 const uint64_t kDeltaDefaultExpirationInSeconds = UINT64_C(604800);
@@ -154,6 +158,9 @@
 
 }  // namespace
 
+const base::Feature kRecurrentInterstitialFeature{
+    "RecurrentInterstitialFeature", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // This helper function gets the dictionary of certificate fingerprints to
 // errors of certificates that have been accepted by the user from the content
 // dictionary that has been passed in. The returned pointer is owned by the the
@@ -488,6 +495,39 @@
   NOTREACHED();
   return false;
 }
+
 void ChromeSSLHostStateDelegate::SetClock(std::unique_ptr<base::Clock> clock) {
   clock_ = std::move(clock);
 }
+
+void ChromeSSLHostStateDelegate::DidDisplayErrorPage(int error) {
+  if (error != net::ERR_CERT_SYMANTEC_LEGACY &&
+      error != net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED) {
+    return;
+  }
+  const auto count_it = recurrent_errors_.find(error);
+  if (count_it == recurrent_errors_.end()) {
+    recurrent_errors_[error] = 1;
+    return;
+  }
+  if (count_it->second >= base::GetFieldTrialParamByFeatureAsInt(
+                              kRecurrentInterstitialFeature,
+                              kRecurrentInterstitialThresholdParam,
+                              kRecurrentInterstitialDefaultThreshold)) {
+    return;
+  }
+  recurrent_errors_[error] = count_it->second + 1;
+}
+
+bool ChromeSSLHostStateDelegate::HasSeenRecurrentErrors(int error) const {
+  if (!base::FeatureList::IsEnabled(kRecurrentInterstitialFeature)) {
+    return false;
+  }
+  const auto count = recurrent_errors_.find(error);
+  if (count == recurrent_errors_.end())
+    return false;
+  return count->second >= base::GetFieldTrialParamByFeatureAsInt(
+                              kRecurrentInterstitialFeature,
+                              kRecurrentInterstitialThresholdParam,
+                              kRecurrentInterstitialDefaultThreshold);
+}
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
index 13dc842..893674d9 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 
+#include "base/feature_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/time/time.h"
@@ -20,10 +21,13 @@
 class DictionaryValue;
 }  //  namespace base
 
-// Tracks whether the user has allowed a certificate error exception for a
-// specific site, SSL fingerprint, and error. Based on command-line flags and
-// experimental group, remembers this decision either until end-of-session or
-// for a particular length of time.
+extern const base::Feature kRecurrentInterstitialFeature;
+
+// Tracks state related to certificate and SSL errors. This state includes:
+// - certificate error exceptions (which are remembered for a particular length
+//   of time depending on experimental groups)
+// - mixed content exceptions
+// - when errors have recurred multiple times
 class ChromeSSLHostStateDelegate : public content::SSLHostStateDelegate {
  public:
   explicit ChromeSSLHostStateDelegate(Profile* profile);
@@ -63,6 +67,18 @@
   // error combination exception is allowed, use QueryPolicy().
   bool HasAllowException(const std::string& host) const override;
 
+  // Called when an error page is displayed for a given error code |error|.
+  // Tracks whether an error of interest has recurred over a threshold number of
+  // times.
+  void DidDisplayErrorPage(int error);
+
+  // Returns true if DidDisplayErrorPage() has been called over a threshold
+  // number of times for a particular error. Always returns false if
+  // |kRecurrentInterstitialFeature| is not enabled. Only certain error codes of
+  // interest are tracked, so this may return false for an error code that has
+  // recurred.
+  bool HasSeenRecurrentErrors(int error) const;
+
  protected:
   // SetClock takes ownership of the passed in clock.
   void SetClock(std::unique_ptr<base::Clock> clock);
@@ -148,6 +164,10 @@
   // https://crbug.com/418631 for more details.
   const std::string current_expiration_guid_;
 
+  // Tracks how many times an error page has been shown for a given error, up
+  // to a certain threshold value.
+  std::map<int /* error code */, int /* count */> recurrent_errors_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeSSLHostStateDelegate);
 };
 
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
index 10193b9c..00a4120 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
@@ -345,6 +345,30 @@
   EXPECT_EQ(ContentSettingsPattern::Wildcard(), settings[0].secondary_pattern);
 }
 
+// Tests that ChromeSSLHostStateDelegate::HasSeenRecurrentErrors returns true
+// after seeing an error of interest multiple times.
+IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, HasSeenRecurrentErrors) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(kRecurrentInterstitialFeature,
+                                                  {{"threshold", "2"}});
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
+  content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
+  ChromeSSLHostStateDelegate* chrome_state =
+      static_cast<ChromeSSLHostStateDelegate*>(state);
+
+  chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
+  EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
+      net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
+  chrome_state->DidDisplayErrorPage(net::ERR_CERT_SYMANTEC_LEGACY);
+  EXPECT_FALSE(chrome_state->HasSeenRecurrentErrors(
+      net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
+  chrome_state->DidDisplayErrorPage(net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED);
+  EXPECT_TRUE(chrome_state->HasSeenRecurrentErrors(
+      net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
+}
+
 class ForgetAtSessionEndSSLHostStateDelegateTest
     : public ChromeSSLHostStateDelegateTest {
  protected:
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
index 5a7db50..ceb82253 100644
--- a/chrome/browser/ssl/ssl_blocking_page.cc
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -18,6 +18,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_preferences_util.h"
 #include "chrome/browser/ssl/cert_report_helper.h"
+#include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h"
+#include "chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h"
 #include "chrome/browser/ssl/ssl_cert_reporter.h"
 #include "chrome/browser/ssl/ssl_error_controller_client.h"
 #include "chrome/common/chrome_switches.h"
@@ -85,6 +87,11 @@
                                     overridable, is_superfish));
   metrics_helper.get()->StartRecordingCaptivePortalMetrics(overridable);
 
+  ChromeSSLHostStateDelegate* state =
+      ChromeSSLHostStateDelegateFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents->GetBrowserContext()));
+  state->DidDisplayErrorPage(cert_error);
+
   return new SSLBlockingPage(web_contents, cert_error, ssl_info, request_url,
                              options_mask, time_triggered, support_url,
                              std::move(ssl_cert_reporter), overridable,
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 9b046f50..43ca383 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -21,16 +20,11 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_attributes_entry.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/supervised_user/experimental/supervised_user_filtering_switches.h"
 #include "chrome/browser/supervised_user/permission_request_creator.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/supervised_user/supervised_user_features.h"
-#include "chrome/browser/supervised_user/supervised_user_navigation_throttle.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
@@ -40,20 +34,14 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/signin_manager_base.h"
-#include "components/signin/core/browser/signin_switches.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -78,9 +66,7 @@
 #include "extensions/browser/extension_system.h"
 #endif
 
-using base::DictionaryValue;
 using base::UserMetricsAction;
-using content::BrowserThread;
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 using extensions::Extension;
@@ -343,14 +329,10 @@
 
 #if !defined(OS_ANDROID)
 void SupervisedUserService::InitSync(const std::string& refresh_token) {
-  StartSetupSync();
-
   ProfileOAuth2TokenService* token_service =
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
   token_service->UpdateCredentials(supervised_users::kSupervisedUserPseudoEmail,
                                    refresh_token);
-
-  FinishSetupSyncWhenReady();
 }
 #endif  // !defined(OS_ANDROID)
 
@@ -394,7 +376,6 @@
       profile_(profile),
       active_(false),
       delegate_(NULL),
-      waiting_for_sync_initialization_(false),
       is_profile_active_(false),
       did_init_(false),
       did_shutdown_(false),
@@ -417,19 +398,10 @@
   if (!delegate_ || !delegate_->SetActive(active_)) {
     if (active_) {
 #if !defined(OS_ANDROID)
-      base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-      if (command_line->HasSwitch(switches::kSupervisedUserSyncToken)) {
-        InitSync(
-            command_line->GetSwitchValueASCII(
-                switches::kSupervisedUserSyncToken));
-      }
-
       ProfileOAuth2TokenService* token_service =
           ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
       token_service->LoadCredentials(
           supervised_users::kSupervisedUserPseudoEmail);
-
-      SetupSync();
 #else
       NOTREACHED();
 #endif
@@ -522,9 +494,6 @@
       observer.OnURLFilterChanged();
 
 #if !defined(OS_ANDROID)
-    if (waiting_for_sync_initialization_)
-      ProfileSyncServiceFactory::GetForProfile(profile_)->RemoveObserver(this);
-
     // TODO(bauerb): Get rid of the platform-specific #ifdef here.
     // http://crbug.com/313377
     BrowserList::RemoveObserver(this);
@@ -532,51 +501,6 @@
   }
 }
 
-#if !defined(OS_ANDROID)
-void SupervisedUserService::SetupSync() {
-  StartSetupSync();
-  FinishSetupSyncWhenReady();
-}
-
-void SupervisedUserService::StartSetupSync() {
-  // Tell the sync service that setup is in progress so we don't start syncing
-  // until we've finished configuration.
-  sync_blocker_ = ProfileSyncServiceFactory::GetForProfile(profile_)
-                      ->GetSetupInProgressHandle();
-}
-
-void SupervisedUserService::FinishSetupSyncWhenReady() {
-  // If we're already waiting for the sync engine, there's nothing to do here.
-  if (waiting_for_sync_initialization_)
-    return;
-
-  // Continue in FinishSetupSync() once the sync engine has been initialized.
-  browser_sync::ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetForProfile(profile_);
-  if (service->IsEngineInitialized()) {
-    FinishSetupSync();
-  } else {
-    service->AddObserver(this);
-    waiting_for_sync_initialization_ = true;
-  }
-}
-
-void SupervisedUserService::FinishSetupSync() {
-  browser_sync::ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetForProfile(profile_);
-  DCHECK(service->IsEngineInitialized());
-
-  // Sync nothing (except types which are set via GetPreferredDataTypes).
-  bool sync_everything = false;
-  syncer::ModelTypeSet synced_datatypes;
-  service->OnUserChoseDatatypes(sync_everything, synced_datatypes);
-
-  // Notify ProfileSyncService that we are done with configuration.
-  sync_blocker_.reset();
-  service->SetFirstSetupComplete();
-}
-#endif
-
 bool SupervisedUserService::ProfileIsSupervised() const {
   return profile_->IsSupervised();
 }
@@ -837,11 +761,9 @@
     base::RecordAction(UserMetricsAction("ManagedUsers_QuitBrowser"));
   }
   SetActive(false);
-  sync_blocker_.reset();
 
   browser_sync::ProfileSyncService* sync_service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
-
   // Can be null in tests.
   if (sync_service)
     sync_service->RemovePreferenceProvider(this);
@@ -1121,19 +1043,6 @@
 }
 
 #if !defined(OS_ANDROID)
-void SupervisedUserService::OnStateChanged(syncer::SyncService* sync) {
-  if (waiting_for_sync_initialization_ && sync->IsEngineInitialized()) {
-    waiting_for_sync_initialization_ = false;
-    sync->RemoveObserver(this);
-    FinishSetupSync();
-    return;
-  }
-
-  DLOG_IF(ERROR, sync->GetAuthError().state() ==
-                     GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)
-      << "Credentials rejected";
-}
-
 void SupervisedUserService::OnBrowserSetLastActive(Browser* browser) {
   bool profile_became_active = profile_->IsSameProfile(browser->profile());
   if (!is_profile_active_ && profile_became_active)
diff --git a/chrome/browser/supervised_user/supervised_user_service.h b/chrome/browser/supervised_user/supervised_user_service.h
index 710ac0e6..edf917f 100644
--- a/chrome/browser/supervised_user/supervised_user_service.h
+++ b/chrome/browser/supervised_user/supervised_user_service.h
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "components/sync/driver/sync_service_observer.h"
 #include "components/sync/driver/sync_type_preference_provider.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -60,10 +59,6 @@
 class ExtensionRegistry;
 }
 
-namespace syncer {
-class SyncSetupInProgressHandle;
-}
-
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -78,7 +73,6 @@
 #endif
                               public syncer::SyncTypePreferenceProvider,
 #if !defined(OS_ANDROID)
-                              public syncer::SyncServiceObserver,
                               public BrowserListObserver,
 #endif
                               public SupervisedUserURLFilter::Observer {
@@ -200,9 +194,6 @@
   syncer::ModelTypeSet GetPreferredDataTypes() const override;
 
 #if !defined(OS_ANDROID)
-  // syncer::SyncServiceObserver implementation:
-  void OnStateChanged(syncer::SyncService* sync) override;
-
   // BrowserListObserver implementation:
   void OnBrowserSetLastActive(Browser* browser) override;
 #endif  // !defined(OS_ANDROID)
@@ -231,13 +222,6 @@
 
   void SetActive(bool active);
 
-#if !defined(OS_ANDROID)
-  void SetupSync();
-  void StartSetupSync();
-  void FinishSetupSyncWhenReady();
-  void FinishSetupSync();
-#endif
-
   bool ProfileIsSupervised() const;
 
   void OnCustodianInfoChanged();
@@ -361,8 +345,6 @@
 
   PrefChangeRegistrar pref_change_registrar_;
 
-  // True iff we're waiting for the Sync service to be initialized.
-  bool waiting_for_sync_initialization_;
   bool is_profile_active_;
 
   std::vector<NavigationBlockedCallback> navigation_blocked_callbacks_;
@@ -406,9 +388,6 @@
 
   base::ObserverList<SupervisedUserServiceObserver> observer_list_;
 
-  // Prevents Sync from running until configuration is complete.
-  std::unique_ptr<syncer::SyncSetupInProgressHandle> sync_blocker_;
-
   base::WeakPtrFactory<SupervisedUserService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SupervisedUserService);
diff --git a/chrome/browser/task_manager/providers/web_contents/background_contents_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/background_contents_tag_browsertest.cc
index bc00df6a..4c144415 100644
--- a/chrome/browser/task_manager/providers/web_contents/background_contents_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/background_contents_tag_browsertest.cc
@@ -19,7 +19,7 @@
 // properly and the TagsManager records these tags. It is also used to test that
 // the WebContentsTaskProvider will be able to provide the appropriate
 // BackgroundContentsTask.
-class BackgroundContentsTagTest : public ExtensionBrowserTest {
+class BackgroundContentsTagTest : public extensions::ExtensionBrowserTest {
  public:
   BackgroundContentsTagTest() {}
   ~BackgroundContentsTagTest() override {}
@@ -41,10 +41,10 @@
   }
 
  protected:
-  // ExtensionBrowserTest:
+  // extensions::ExtensionBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Pass flags to make testing apps easier.
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     test_data_dir_ = test_data_dir_.AppendASCII("api_test");
     command_line->AppendSwitch(switches::kDisableRendererBackgrounding);
     command_line->AppendSwitch(switches::kDisablePopupBlocking);
diff --git a/chrome/browser/task_manager/providers/web_contents/extension_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/extension_tag_browsertest.cc
index 0a73517..9d0b6be8 100644
--- a/chrome/browser/task_manager/providers/web_contents/extension_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/extension_tag_browsertest.cc
@@ -18,15 +18,15 @@
 
 namespace task_manager {
 
-class ExtensionTagsTest : public ExtensionBrowserTest {
+class ExtensionTagsTest : public extensions::ExtensionBrowserTest {
  public:
   ExtensionTagsTest() {}
   ~ExtensionTagsTest() override {}
 
  protected:
-  // ExtensionBrowserTest:
+  // extensions::ExtensionBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 
     // Do not launch device discovery process.
     command_line->AppendSwitch(switches::kDisableDeviceDiscoveryNotifications);
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index b1dc996..4117640 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -77,7 +77,7 @@
 
 }  // namespace
 
-class TaskManagerBrowserTest : public ExtensionBrowserTest {
+class TaskManagerBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   TaskManagerBrowserTest() {}
   ~TaskManagerBrowserTest() override {}
@@ -117,7 +117,7 @@
 
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 
     // Do not launch device discovery process.
     command_line->AppendSwitch(switches::kDisableDeviceDiscoveryNotifications);
@@ -125,11 +125,11 @@
 
   void TearDownOnMainThread() override {
     model_.reset();
-    ExtensionBrowserTest::TearDownOnMainThread();
+    extensions::ExtensionBrowserTest::TearDownOnMainThread();
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
 
     // Add content/test/data so we can use cross_site_iframe_factory.html
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 76280b6..ae5065c 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -160,14 +160,16 @@
 
     case ThemeProperties::COLOR_TAB_TEXT:
     case ThemeProperties::COLOR_BOOKMARK_TEXT:
+      return incognito ? gfx::kGoogleGrey100 : gfx::kGoogleGrey800;
+
     case ThemeProperties::COLOR_TAB_CLOSE_BUTTON_ACTIVE:
     case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON:
-      return incognito ? gfx::kGoogleGrey100 : gfx::kGoogleGrey800;
+      return incognito ? gfx::kGoogleGrey100 : gfx::kChromeIconGrey;
 
     case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT:
     case ThemeProperties::COLOR_TAB_CLOSE_BUTTON_INACTIVE:
     case ThemeProperties::COLOR_TAB_ALERT_AUDIO:
-      return incognito ? gfx::kGoogleGrey400 : gfx::kGoogleGrey700;
+      return incognito ? gfx::kGoogleGrey400 : gfx::kChromeIconGrey;
 
     case ThemeProperties::COLOR_TAB_CLOSE_BUTTON_BACKGROUND_HOVER:
       return incognito ? gfx::kGoogleGrey700 : gfx::kGoogleGrey200;
diff --git a/chrome/browser/themes/theme_service_browsertest.cc b/chrome/browser/themes/theme_service_browsertest.cc
index 4b0a959..4afc9da 100644
--- a/chrome/browser/themes/theme_service_browsertest.cc
+++ b/chrome/browser/themes/theme_service_browsertest.cc
@@ -28,7 +28,7 @@
          !theme_service.UsingDefaultTheme();
 }
 
-class ThemeServiceBrowserTest : public ExtensionBrowserTest {
+class ThemeServiceBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   ThemeServiceBrowserTest() {
   }
@@ -36,7 +36,7 @@
 
   void SetUp() override {
     extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
-    ExtensionBrowserTest::SetUp();
+    extensions::ExtensionBrowserTest::SetUp();
   }
 
  private:
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0f14f25..e7da097d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1389,8 +1389,6 @@
       "omnibox/chrome_omnibox_navigation_observer.h",
       "omnibox/clipboard_utils.cc",
       "omnibox/clipboard_utils.h",
-      "omnibox/favicon_cache.cc",
-      "omnibox/favicon_cache.h",
       "omnibox/omnibox_theme.cc",
       "omnibox/omnibox_theme.h",
       "page_info/page_info_dialog.cc",
@@ -1876,8 +1874,6 @@
       "views/extensions/request_file_system_dialog_view.h",
       "views/frame/browser_frame_ash.cc",
       "views/frame/browser_frame_ash.h",
-      "views/frame/browser_frame_header_ash.cc",
-      "views/frame/browser_frame_header_ash.h",
       "views/frame/browser_non_client_frame_view_ash.cc",
       "views/frame/browser_non_client_frame_view_ash.h",
       "views/frame/immersive_context_mus.cc",
diff --git a/chrome/browser/ui/android/infobars/framebust_block_infobar.cc b/chrome/browser/ui/android/infobars/framebust_block_infobar.cc
index e79ce61a..dbaa1c1 100644
--- a/chrome/browser/ui/android/infobars/framebust_block_infobar.cc
+++ b/chrome/browser/ui/android/infobars/framebust_block_infobar.cc
@@ -34,8 +34,10 @@
   if (!owner())
     return;  // We're closing; don't call anything, it might access the owner.
 
+  // Tapping the button means that the user wants to bypass the intervention in
+  // a sticky way, e.g. via content settings.
   DCHECK_EQ(action, InfoBarAndroid::ACTION_OK);
-  delegate_->AcceptIntervention();
+  delegate_->DeclineInterventionSticky();
   RemoveSelf();
 }
 
diff --git a/chrome/browser/ui/app_list/app_context_menu_unittest.cc b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
index 774fbd94..ad4b3123 100644
--- a/chrome/browser/ui/app_list/app_context_menu_unittest.cc
+++ b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
@@ -257,6 +257,46 @@
     if (!platform_app) {
       AddToStates(menu, MenuState(app_list::AppContextMenu::LAUNCH_NEW),
                   &states);
+      if (!features::IsTouchableAppContextMenuEnabled()) {
+        AddSeparator(&states);
+
+        if (extensions::util::CanHostedAppsOpenInWindows() &&
+            extensions::util::IsNewBookmarkAppsEnabled()) {
+          bool checked = launch_type == extensions::LAUNCH_TYPE_WINDOW ||
+                         launch_type == extensions::LAUNCH_TYPE_FULLSCREEN;
+          AddToStates(
+              menu,
+              MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_WINDOW, true,
+                        checked),
+              &states);
+        } else if (!extensions::util::IsNewBookmarkAppsEnabled()) {
+          bool regular_checked = launch_type == extensions::LAUNCH_TYPE_REGULAR;
+
+          AddToStates(
+              menu,
+              MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_REGULAR, true,
+                        regular_checked),
+              &states);
+          AddToStates(
+              menu,
+              MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_PINNED, true,
+                        launch_type == extensions::LAUNCH_TYPE_PINNED),
+              &states);
+          if (extensions::util::CanHostedAppsOpenInWindows()) {
+            AddToStates(
+                menu,
+                MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_WINDOW,
+                          true, launch_type == extensions::LAUNCH_TYPE_WINDOW),
+                &states);
+          }
+          AddToStates(
+              menu,
+              MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_FULLSCREEN,
+                        true,
+                        launch_type == extensions::LAUNCH_TYPE_FULLSCREEN),
+              &states);
+        }
+      }
     }
     if (pinnable != AppListControllerDelegate::NO_PIN) {
       AddSeparator(&states);
@@ -278,44 +318,6 @@
                   &states);
     }
 
-    AddSeparator(&states);
-
-    if (!platform_app) {
-      if (extensions::util::CanHostedAppsOpenInWindows() &&
-          extensions::util::IsNewBookmarkAppsEnabled()) {
-        bool checked = launch_type == extensions::LAUNCH_TYPE_WINDOW ||
-            launch_type == extensions::LAUNCH_TYPE_FULLSCREEN;
-        AddToStates(menu,
-                    MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_WINDOW,
-                              true, checked),
-                    &states);
-      } else if (!extensions::util::IsNewBookmarkAppsEnabled()) {
-        bool regular_checked = launch_type == extensions::LAUNCH_TYPE_REGULAR;
-
-        AddToStates(menu,
-                    MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_REGULAR,
-                              true, regular_checked),
-                    &states);
-        AddToStates(
-            menu,
-            MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_PINNED, true,
-                      launch_type == extensions::LAUNCH_TYPE_PINNED),
-            &states);
-        if (extensions::util::CanHostedAppsOpenInWindows()) {
-          AddToStates(
-              menu,
-              MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_WINDOW, true,
-                        launch_type == extensions::LAUNCH_TYPE_WINDOW),
-              &states);
-        }
-        AddToStates(
-            menu,
-            MenuState(app_list::AppContextMenu::USE_LAUNCH_TYPE_FULLSCREEN,
-                      true, launch_type == extensions::LAUNCH_TYPE_FULLSCREEN),
-            &states);
-      }
-    }
-
     ValidateMenuState(menu_model.get(), states);
   }
 
diff --git a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
index 79030408..3ddb5b42 100644
--- a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
@@ -49,7 +49,8 @@
 }
 
 // Browser Test for AppListController that observes search result changes.
-using AppListControllerSearchResultsBrowserTest = ExtensionBrowserTest;
+using AppListControllerSearchResultsBrowserTest =
+    extensions::ExtensionBrowserTest;
 
 // Test showing search results, and uninstalling one of them while displayed.
 IN_PROC_BROWSER_TEST_F(AppListControllerSearchResultsBrowserTest,
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 1ba9328..e6c2a990 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -903,6 +903,13 @@
   return syncer::SyncError();
 }
 
+void AppListSyncableService::Shutdown() {
+  internal_apps_builder_.reset();
+  crostini_apps_builder_.reset();
+  arc_apps_builder_.reset();
+  apps_builder_.reset();
+}
+
 // AppListSyncableService private
 
 bool AppListSyncableService::ProcessSyncItemSpecifics(
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h
index 78c5245d..6bc7e33 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.h
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.h
@@ -162,6 +162,9 @@
       const base::Location& from_here,
       const syncer::SyncChangeList& change_list) override;
 
+  // KeyedService
+  void Shutdown() override;
+
  private:
   class ModelUpdaterDelegate;
 
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
index 913b104..b909bf8 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service_factory.cc
@@ -86,6 +86,19 @@
 
 content::BrowserContext* AppListSyncableServiceFactory::GetBrowserContextToUse(
     content::BrowserContext* context) const {
+  Profile* const profile = Profile::FromBrowserContext(context);
+  // No service if |context| is not a profile.
+  if (!profile)
+    return nullptr;
+
+  // No service for system profile.
+  if (profile->IsSystemProfile())
+    return nullptr;
+
+  // Use profile as-is for guest session.
+  if (profile->IsGuestSession())
+    return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+
   // This matches the logic in ExtensionSyncServiceFactory, which uses the
   // orginal browser context.
   return chrome::GetBrowserContextRedirectedInIncognito(context);
diff --git a/chrome/browser/ui/app_list/extension_app_context_menu.cc b/chrome/browser/ui/app_list/extension_app_context_menu.cc
index 61be1e0e5..e7b9094 100644
--- a/chrome/browser/ui/app_list/extension_app_context_menu.cc
+++ b/chrome/browser/ui/app_list/extension_app_context_menu.cc
@@ -89,7 +89,38 @@
 
     // First, add the primary actions.
     if (!is_platform_app_) {
-      AddContextMenuOption(menu_model, LAUNCH_NEW, GetLaunchStringId());
+      if (features::IsTouchableAppContextMenuEnabled()) {
+        CreateOpenNewSubmenu(menu_model);
+      } else {
+        AddContextMenuOption(menu_model, LAUNCH_NEW, GetLaunchStringId());
+        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+
+        // When bookmark apps are enabled, hosted apps can only toggle between
+        // USE_LAUNCH_TYPE_WINDOW and USE_LAUNCH_TYPE_REGULAR.
+        if (extensions::util::CanHostedAppsOpenInWindows() &&
+            extensions::util::IsNewBookmarkAppsEnabled()) {
+          // When both flags are enabled, only allow toggling between
+          // USE_LAUNCH_TYPE_WINDOW and USE_LAUNCH_TYPE_REGULAR
+          AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_WINDOW,
+                               IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
+        } else if (!extensions::util::IsNewBookmarkAppsEnabled()) {
+          // When new bookmark apps are disabled, add pinned and full screen
+          // options as well. Add open as window if CanHostedAppsOpenInWindows
+          // is enabled.
+          AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_REGULAR,
+                               IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
+          AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_PINNED,
+                               IDS_APP_CONTEXT_MENU_OPEN_PINNED);
+          if (extensions::util::CanHostedAppsOpenInWindows()) {
+            AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_WINDOW,
+                                 IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
+          }
+          // Even though the launch type is Full Screen it is more accurately
+          // described as Maximized in Ash.
+          AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_FULLSCREEN,
+                               IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
+        }
+      }
     }
 
     // Create default items.
@@ -118,46 +149,11 @@
                          is_platform_app_ ? IDS_APP_LIST_UNINSTALL_ITEM
                                           : IDS_APP_LIST_EXTENSIONS_UNINSTALL);
 
-    if (controller()->CanDoShowAppInfoFlow())
+    if (controller()->CanDoShowAppInfoFlow()) {
       AddContextMenuOption(menu_model, SHOW_APP_INFO,
                            IDS_APP_CONTEXT_MENU_SHOW_INFO);
-
-    if (!features::IsTouchableAppContextMenuEnabled())
-      menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
-
-    if (!is_platform_app_) {
-      // When bookmark apps are enabled, hosted apps can only toggle between
-      // USE_LAUNCH_TYPE_WINDOW and USE_LAUNCH_TYPE_REGULAR.
-      if (extensions::util::CanHostedAppsOpenInWindows() &&
-          extensions::util::IsNewBookmarkAppsEnabled()) {
-        // When both flags are enabled, only allow toggling between
-        // USE_LAUNCH_TYPE_WINDOW and USE_LAUNCH_TYPE_REGULAR
-        AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_WINDOW,
-                             IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
-      } else if (!extensions::util::IsNewBookmarkAppsEnabled()) {
-        // When new bookmark apps are disabled, add pinned and full screen
-        // options as well. Add open as window if CanHostedAppsOpenInWindows
-        // is enabled.
-        AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_REGULAR,
-                             IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
-        AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_PINNED,
-                             IDS_APP_CONTEXT_MENU_OPEN_PINNED);
-        if (extensions::util::CanHostedAppsOpenInWindows()) {
-          AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_WINDOW,
-                               IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
-        }
-        // Even though the launch type is Full Screen it is more accurately
-        // described as Maximized in Ash.
-        AddContextMenuOption(menu_model, USE_LAUNCH_TYPE_FULLSCREEN,
-                             IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
-      }
-    }
   }
-}
-
-bool ExtensionAppContextMenu::IsItemForCommandIdDynamic(int command_id) const {
-  return command_id == LAUNCH_NEW ||
-      AppContextMenu::IsItemForCommandIdDynamic(command_id);
+  }
 }
 
 base::string16 ExtensionAppContextMenu::GetLabelForCommandId(
@@ -187,6 +183,11 @@
   return true;
 }
 
+bool ExtensionAppContextMenu::IsItemForCommandIdDynamic(int command_id) const {
+  return command_id == LAUNCH_NEW ||
+         AppContextMenu::IsItemForCommandIdDynamic(command_id);
+}
+
 bool ExtensionAppContextMenu::IsCommandIdChecked(int command_id) const {
   if (command_id >= USE_LAUNCH_TYPE_COMMAND_START &&
       command_id < USE_LAUNCH_TYPE_COMMAND_END) {
@@ -255,4 +256,22 @@
   }
 }
 
+void ExtensionAppContextMenu::CreateOpenNewSubmenu(
+    ui::SimpleMenuModel* menu_model) {
+  // Touchable extension context menus use an actionable submenu for LAUNCH_NEW.
+  const int kGroupId = 1;
+  open_new_submenu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
+  open_new_submenu_model_->AddRadioItemWithStringId(
+      USE_LAUNCH_TYPE_REGULAR, IDS_APP_LIST_CONTEXT_MENU_NEW_TAB, kGroupId);
+  open_new_submenu_model_->AddRadioItemWithStringId(
+      USE_LAUNCH_TYPE_WINDOW, IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW, kGroupId);
+  const views::MenuConfig& menu_config = views::MenuConfig::instance();
+  const gfx::VectorIcon& icon =
+      GetMenuItemVectorIcon(LAUNCH_NEW, GetLaunchStringId());
+  menu_model->AddActionableSubmenuWithStringIdAndIcon(
+      LAUNCH_NEW, GetLaunchStringId(), open_new_submenu_model_.get(),
+      gfx::CreateVectorIcon(icon, menu_config.touchable_icon_size,
+                            menu_config.touchable_icon_color));
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/extension_app_context_menu.h b/chrome/browser/ui/app_list/extension_app_context_menu.h
index 066d4ff..db1603a 100644
--- a/chrome/browser/ui/app_list/extension_app_context_menu.h
+++ b/chrome/browser/ui/app_list/extension_app_context_menu.h
@@ -52,8 +52,15 @@
   }
 
  private:
+  // Creates the actionable submenu for LAUNCH_NEW.
+  void CreateOpenNewSubmenu(ui::SimpleMenuModel* menu_model);
+
   bool is_platform_app_ = false;
 
+  // The MenuModel used to control LAUNCH_NEW's icon, label, and
+  // execution when touchable app context menus are enabled.
+  std::unique_ptr<ui::SimpleMenuModel> open_new_submenu_model_;
+
   std::unique_ptr<extensions::ContextMenuMatcher> extension_menu_items_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionAppContextMenu);
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 1e4b685..cc47bbf 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -148,6 +148,8 @@
         ash::kCanConsumeSystemKeysKey,
         ash::mojom::kCanConsumeSystemKeys_Property,
         aura::PropertyConverter::CreateAcceptAnyValueCallback());
+    converter->RegisterImageSkiaProperty(
+        ash::kFrameImageActiveKey, ash::mojom::kFrameImageActive_Property);
     converter->RegisterPrimitiveProperty(
         ash::kHideShelfWhenFullscreenKey,
         ash::mojom::kHideShelfWhenFullscreen_Property,
@@ -180,6 +182,10 @@
         ash::kRestoreWindowStateTypeOverrideKey,
         ash::mojom::kRestoreWindowStateTypeOverride_Property,
         base::BindRepeating(&ash::IsValidWindowStateType));
+    converter->RegisterPrimitiveProperty(
+        ash::kWindowTitleShownKey,
+        ui::mojom::WindowManager::kWindowTitleShown_Property,
+        aura::PropertyConverter::CreateAcceptAnyValueCallback());
 
     mus_client->SetMusPropertyMirror(
         std::make_unique<ash::MusPropertyMirrorAsh>());
diff --git a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
index b6b869cb..12890a8 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_launcher_browsertest.cc
@@ -154,7 +154,7 @@
 
 }  // namespace
 
-class ArcAppLauncherBrowserTest : public ExtensionBrowserTest {
+class ArcAppLauncherBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   ArcAppLauncherBrowserTest() {}
   ~ArcAppLauncherBrowserTest() override {}
@@ -162,12 +162,12 @@
  protected:
   // content::BrowserTestBase:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     arc::SetArcAvailableCommandLineForTesting(command_line);
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
     arc::ArcSessionManager::SetUiEnabledForTesting(false);
   }
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 6a4ef1f..fc868a2 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -230,7 +230,7 @@
   RIP_OFF_ITEM_AND_DONT_RELEASE_MOUSE,
 };
 
-class ShelfAppBrowserTest : public ExtensionBrowserTest {
+class ShelfAppBrowserTest : public extensions::ExtensionBrowserTest {
  protected:
   ShelfAppBrowserTest() {}
 
@@ -244,7 +244,7 @@
 
     controller_ = ChromeLauncherController::instance();
     ASSERT_TRUE(controller_);
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
   }
 
   size_t NumberOfDetectedLauncherBrowsers(bool show_all_tabs) {
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index 356eb5c..b75c60d 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -24,6 +24,8 @@
 #include "content/public/common/context_menu_params.h"
 #include "extensions/browser/extension_prefs.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/menu/menu_config.h"
 
 namespace {
 
@@ -48,84 +50,6 @@
   std::move(callback).Run(std::move(menu_model));
 }
 
-void ExtensionLauncherContextMenu::BuildMenu(ui::SimpleMenuModel* menu_model) {
-  extension_items_.reset(new extensions::ContextMenuMatcher(
-      controller()->profile(), this, menu_model,
-      base::Bind(MenuItemHasLauncherContext)));
-  if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
-    // V1 apps can be started from the menu - but V2 apps should not.
-    if (!controller()->IsPlatformApp(item().id)) {
-      int string_id = (GetLaunchType() == extensions::LAUNCH_TYPE_PINNED ||
-                       GetLaunchType() == extensions::LAUNCH_TYPE_REGULAR)
-                          ? IDS_APP_LIST_CONTEXT_MENU_NEW_TAB
-                          : IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW;
-      AddContextMenuOption(menu_model, MENU_OPEN_NEW, string_id);
-      if (!features::IsTouchableAppContextMenuEnabled())
-        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
-    }
-
-    AddPinMenu(menu_model);
-
-    if (controller()->IsOpen(item().id)) {
-      AddContextMenuOption(menu_model, MENU_CLOSE,
-                           IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
-    }
-
-    if (!controller()->IsPlatformApp(item().id) &&
-        item().type == ash::TYPE_PINNED_APP) {
-      if (!features::IsTouchableAppContextMenuEnabled())
-        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
-      if (extensions::util::IsNewBookmarkAppsEnabled()) {
-        // With bookmark apps enabled, hosted apps launch in a window by
-        // default. This menu item is re-interpreted as a single, toggle-able
-        // option to launch the hosted app as a tab.
-        AddContextMenuOption(menu_model, LAUNCH_TYPE_WINDOW,
-                             IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
-      } else {
-        AddContextMenuOption(menu_model, LAUNCH_TYPE_REGULAR_TAB,
-                             IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
-        AddContextMenuOption(menu_model, LAUNCH_TYPE_PINNED_TAB,
-                             IDS_APP_CONTEXT_MENU_OPEN_PINNED);
-        AddContextMenuOption(menu_model, LAUNCH_TYPE_WINDOW,
-                             IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
-        // Even though the launch type is Full Screen it is more accurately
-        // described as Maximized in Ash.
-        AddContextMenuOption(menu_model, LAUNCH_TYPE_FULLSCREEN,
-                             IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
-      }
-    }
-  } else if (item().type == ash::TYPE_BROWSER_SHORTCUT) {
-    AddContextMenuOption(menu_model, MENU_NEW_WINDOW, IDS_APP_LIST_NEW_WINDOW);
-    if (!controller()->profile()->IsGuestSession()) {
-      AddContextMenuOption(menu_model, MENU_NEW_INCOGNITO_WINDOW,
-                           IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
-    }
-    if (!BrowserShortcutLauncherItemController(controller()->shelf_model())
-             .IsListOfActiveBrowserEmpty()) {
-      AddContextMenuOption(menu_model, MENU_CLOSE,
-                           IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
-    }
-  } else if (item().type == ash::TYPE_DIALOG) {
-    AddContextMenuOption(menu_model, MENU_CLOSE,
-                         IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
-  } else if (controller()->IsOpen(item().id)) {
-    AddContextMenuOption(menu_model, MENU_CLOSE,
-                         IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
-  }
-  if (!features::IsTouchableAppContextMenuEnabled())
-    menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
-  if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
-    const extensions::MenuItem::ExtensionKey app_key(item().id.app_id);
-    if (!app_key.empty()) {
-      int index = 0;
-      extension_items_->AppendExtensionItems(app_key, base::string16(), &index,
-                                             false);  // is_action_menu
-      if (!features::IsTouchableAppContextMenuEnabled())
-        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
-    }
-  }
-}
-
 bool ExtensionLauncherContextMenu::IsCommandIdChecked(int command_id) const {
   switch (command_id) {
     case LAUNCH_TYPE_PINNED_TAB:
@@ -213,6 +137,103 @@
   }
 }
 
+void ExtensionLauncherContextMenu::CreateOpenNewSubmenu(
+    ui::SimpleMenuModel* menu_model) {
+  // Touchable extension context menus use an actionable submenu for
+  // MENU_OPEN_NEW.
+  const gfx::VectorIcon& icon =
+      GetMenuItemVectorIcon(MENU_OPEN_NEW, GetLaunchTypeStringId());
+  const views::MenuConfig& menu_config = views::MenuConfig::instance();
+  const int kGroupId = 1;
+  open_new_submenu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
+  open_new_submenu_model_->AddRadioItemWithStringId(
+      LAUNCH_TYPE_REGULAR_TAB, IDS_APP_LIST_CONTEXT_MENU_NEW_TAB, kGroupId);
+  open_new_submenu_model_->AddRadioItemWithStringId(
+      LAUNCH_TYPE_WINDOW, IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW, kGroupId);
+  menu_model->AddActionableSubmenuWithStringIdAndIcon(
+      MENU_OPEN_NEW, GetLaunchTypeStringId(), open_new_submenu_model_.get(),
+      gfx::CreateVectorIcon(icon, menu_config.touchable_icon_size,
+                            menu_config.touchable_icon_color));
+}
+
+void ExtensionLauncherContextMenu::BuildMenu(ui::SimpleMenuModel* menu_model) {
+  extension_items_.reset(new extensions::ContextMenuMatcher(
+      controller()->profile(), this, menu_model,
+      base::Bind(MenuItemHasLauncherContext)));
+  if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
+    // V1 apps can be started from the menu - but V2 apps should not.
+    if (!controller()->IsPlatformApp(item().id)) {
+      if (features::IsTouchableAppContextMenuEnabled()) {
+        CreateOpenNewSubmenu(menu_model);
+      } else {
+        AddContextMenuOption(menu_model, MENU_OPEN_NEW,
+                             GetLaunchTypeStringId());
+        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+
+        // Touchable app context menus do not include these check items, their
+        // functionality is achieved by an actionable submenu.
+        if (item().type == ash::TYPE_PINNED_APP) {
+          if (extensions::util::IsNewBookmarkAppsEnabled()) {
+            // With bookmark apps enabled, hosted apps launch in a window by
+            // default. This menu item is re-interpreted as a single,
+            // toggle-able option to launch the hosted app as a tab.
+            AddContextMenuOption(menu_model, LAUNCH_TYPE_WINDOW,
+                                 IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
+          } else {
+            AddContextMenuOption(menu_model, LAUNCH_TYPE_REGULAR_TAB,
+                                 IDS_APP_CONTEXT_MENU_OPEN_REGULAR);
+            AddContextMenuOption(menu_model, LAUNCH_TYPE_PINNED_TAB,
+                                 IDS_APP_CONTEXT_MENU_OPEN_PINNED);
+            AddContextMenuOption(menu_model, LAUNCH_TYPE_WINDOW,
+                                 IDS_APP_CONTEXT_MENU_OPEN_WINDOW);
+            // Even though the launch type is Full Screen it is more accurately
+            // described as Maximized in Ash.
+            AddContextMenuOption(menu_model, LAUNCH_TYPE_FULLSCREEN,
+                                 IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED);
+          }
+          menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+        }
+      }
+    }
+
+    AddPinMenu(menu_model);
+
+    if (controller()->IsOpen(item().id)) {
+      AddContextMenuOption(menu_model, MENU_CLOSE,
+                           IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
+    }
+  } else if (item().type == ash::TYPE_BROWSER_SHORTCUT) {
+    AddContextMenuOption(menu_model, MENU_NEW_WINDOW, IDS_APP_LIST_NEW_WINDOW);
+    if (!controller()->profile()->IsGuestSession()) {
+      AddContextMenuOption(menu_model, MENU_NEW_INCOGNITO_WINDOW,
+                           IDS_APP_LIST_NEW_INCOGNITO_WINDOW);
+    }
+    if (!BrowserShortcutLauncherItemController(controller()->shelf_model())
+             .IsListOfActiveBrowserEmpty()) {
+      AddContextMenuOption(menu_model, MENU_CLOSE,
+                           IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
+    }
+  } else if (item().type == ash::TYPE_DIALOG) {
+    AddContextMenuOption(menu_model, MENU_CLOSE,
+                         IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
+  } else if (controller()->IsOpen(item().id)) {
+    AddContextMenuOption(menu_model, MENU_CLOSE,
+                         IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
+  }
+  if (!features::IsTouchableAppContextMenuEnabled())
+    menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+  if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
+    const extensions::MenuItem::ExtensionKey app_key(item().id.app_id);
+    if (!app_key.empty()) {
+      int index = 0;
+      extension_items_->AppendExtensionItems(app_key, base::string16(), &index,
+                                             false);  // is_action_menu
+      if (!features::IsTouchableAppContextMenuEnabled())
+        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+    }
+  }
+}
+
 extensions::LaunchType ExtensionLauncherContextMenu::GetLaunchType() const {
   const extensions::Extension* extension =
       GetExtensionForAppID(item().id.app_id, controller()->profile());
@@ -228,3 +249,10 @@
 void ExtensionLauncherContextMenu::SetLaunchType(extensions::LaunchType type) {
   extensions::SetLaunchType(controller()->profile(), item().id.app_id, type);
 }
+
+int ExtensionLauncherContextMenu::GetLaunchTypeStringId() const {
+  return (GetLaunchType() == extensions::LAUNCH_TYPE_PINNED ||
+          GetLaunchType() == extensions::LAUNCH_TYPE_REGULAR)
+             ? IDS_APP_LIST_CONTEXT_MENU_NEW_TAB
+             : IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW;
+}
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
index e726c2c9..c76200c 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
@@ -32,12 +32,22 @@
   void ExecuteCommand(int command_id, int event_flags) override;
 
  private:
+  // Creates the actionable submenu for MENU_OPEN_NEW.
+  void CreateOpenNewSubmenu(ui::SimpleMenuModel* menu_model);
+
   void BuildMenu(ui::SimpleMenuModel* menu_model);
 
   // Helpers to get and set the launch type for the extension item.
   extensions::LaunchType GetLaunchType() const;
   void SetLaunchType(extensions::LaunchType launch_type);
 
+  // Helper to get the launch type string id.
+  int GetLaunchTypeStringId() const;
+
+  // The MenuModel used to control MENU_OPEN_NEW's icon, label, and
+  // execution when touchable app context menus are enabled.
+  std::unique_ptr<ui::SimpleMenuModel> open_new_submenu_model_;
+
   std::unique_ptr<extensions::ContextMenuMatcher> extension_items_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionLauncherContextMenu);
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index e424877..1dc36a7 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -171,7 +171,7 @@
                               menu_config.touchable_icon_color));
     return;
   }
-  // If the MenuType is a Check item.
+  // If the MenuType is a check item.
   if (type == LAUNCH_TYPE_REGULAR_TAB || type == LAUNCH_TYPE_PINNED_TAB ||
       type == LAUNCH_TYPE_WINDOW || type == LAUNCH_TYPE_FULLSCREEN) {
     menu_model->AddCheckItemWithStringId(type, string_id);
diff --git a/chrome/browser/ui/ash/network/networking_config_delegate_chromeos_browsertest.cc b/chrome/browser/ui/ash/network/networking_config_delegate_chromeos_browsertest.cc
index f845609..a6ed816 100644
--- a/chrome/browser/ui/ash/network/networking_config_delegate_chromeos_browsertest.cc
+++ b/chrome/browser/ui/ash/network/networking_config_delegate_chromeos_browsertest.cc
@@ -20,7 +20,7 @@
 
 namespace {
 
-using NetworkingConfigDelegateChromeosTest = ExtensionBrowserTest;
+using NetworkingConfigDelegateChromeosTest = extensions::ExtensionBrowserTest;
 
 // Tests that an extension registering itself as handling a Wi-Fi SSID updates
 // the ash system tray network item.
diff --git a/chrome/browser/ui/blocked_content/tab_under_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/tab_under_blocker_browsertest.cc
index f0a42cc..762d3ce 100644
--- a/chrome/browser/ui/blocked_content/tab_under_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/tab_under_blocker_browsertest.cc
@@ -41,7 +41,7 @@
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #endif
 
-class TabUnderBlockerBrowserTest : public ExtensionBrowserTest {
+class TabUnderBlockerBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   TabUnderBlockerBrowserTest() {
     EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
@@ -52,7 +52,7 @@
   ~TabUnderBlockerBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     scoped_feature_list_.InitAndEnableFeature(
         TabUnderNavigationThrottle::kBlockTabUnders);
     host_resolver()->AddRule("*", "127.0.0.1");
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index ee9ce25..5d9550d4 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -62,6 +62,7 @@
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/tabs/pinned_tab_codec.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views_mode_controller.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -115,6 +116,7 @@
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
+#include "ui/base/ui_base_features.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -330,10 +332,10 @@
 
 }  // namespace
 
-class BrowserTest : public ExtensionBrowserTest {
+class BrowserTest : public extensions::ExtensionBrowserTest {
  protected:
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
   }
 
@@ -2626,6 +2628,12 @@
 // Test to ensure the bounds of popup, devtool, and app windows are properly
 // restored.
 IN_PROC_BROWSER_TEST_F(BrowserTest, TestPopupBounds) {
+#if BUILDFLAG(MAC_VIEWS_BROWSER)
+  // The size computation on popups is wrong in MacViews:
+  // https://crbug.com/834908.
+  if (!views_mode_controller::IsViewsBrowserCocoa())
+    return;
+#endif
   // TODO(tdanderson|pkasting): Change this to verify that the contents bounds
   // set by params.initial_bounds are the same as the contents bounds in the
   // initialized window. See crbug.com/585856.
diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc
index b171e8d..53044f1 100644
--- a/chrome/browser/ui/browser_list.cc
+++ b/chrome/browser/ui/browser_list.cc
@@ -36,6 +36,15 @@
   return browsers_to_close;
 }
 
+BrowserList::BrowserVector GetIncognitoBrowsersToClose(Profile* profile) {
+  BrowserList::BrowserVector browsers_to_close;
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (browser->profile() == profile)
+      browsers_to_close.push_back(browser);
+  }
+  return browsers_to_close;
+}
+
 }  // namespace
 
 // static
@@ -154,6 +163,18 @@
 }
 
 // static
+void BrowserList::CloseAllBrowsersWithIncognitoProfile(
+    Profile* profile,
+    const CloseCallback& on_close_success,
+    const CloseCallback& on_close_aborted,
+    bool skip_beforeunload) {
+  DCHECK(profile->IsOffTheRecord());
+  TryToCloseBrowserList(GetIncognitoBrowsersToClose(profile), on_close_success,
+                        on_close_aborted, profile->GetPath(),
+                        skip_beforeunload);
+}
+
+// static
 void BrowserList::TryToCloseBrowserList(const BrowserVector& browsers_to_close,
                                         const CloseCallback& on_close_success,
                                         const CloseCallback& on_close_aborted,
diff --git a/chrome/browser/ui/browser_list.h b/chrome/browser/ui/browser_list.h
index 80d883f..31248f31 100644
--- a/chrome/browser/ui/browser_list.h
+++ b/chrome/browser/ui/browser_list.h
@@ -108,6 +108,14 @@
                                           const CloseCallback& on_close_aborted,
                                           bool skip_beforeunload);
 
+  // Similarly to CloseAllBrowsersWithProfile, but DCHECK's that profile is
+  // Off-the-Record and doesn't close browsers with the original profile.
+  static void CloseAllBrowsersWithIncognitoProfile(
+      Profile* profile,
+      const CloseCallback& on_close_success,
+      const CloseCallback& on_close_aborted,
+      bool skip_beforeunload);
+
   // Returns true if at least one incognito session is active across all
   // desktops.
   static bool IsIncognitoSessionActive();
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
index 72b15bc..a9370c5 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
@@ -557,15 +557,6 @@
   EXPECT_EQ(can_fullscreen, !!([ns_window collectionBehavior] &
                                NSWindowCollectionBehaviorFullScreenPrimary));
 
-  // In OSX 10.10+, the zoom button performs the zoom action rather than the
-  // fullscreen action. The above check that collectionBehavior does not include
-  // NSWindowCollectionBehaviorFullScreenPrimary is sufficient to determine that
-  // the window can't be fullscreened.
-  if (base::mac::IsOS10_9()) {
-    EXPECT_EQ(can_fullscreen,
-              [[ns_window standardWindowButton:NSWindowZoomButton] isEnabled]);
-  }
-
   // Set a maximum size.
   app_window->SetContentSizeConstraints(gfx::Size(), gfx::Size(200, 201));
   EXPECT_EQ(200, [ns_window contentMaxSize].width);
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
index 4d51205..f54d688 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
@@ -233,13 +233,13 @@
 
 @end
 
-class BrowserActionButtonUiTest : public ExtensionBrowserTest {
+class BrowserActionButtonUiTest : public extensions::ExtensionBrowserTest {
  protected:
   BrowserActionButtonUiTest() {}
   ~BrowserActionButtonUiTest() override {}
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     toolbarController_ =
         [[BrowserWindowController
             browserWindowControllerForWindow:browser()->
@@ -251,13 +251,13 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     ToolbarActionsBar::disable_animations_for_testing_ = true;
   }
 
   void TearDownOnMainThread() override {
     ToolbarActionsBar::disable_animations_for_testing_ = false;
-    ExtensionBrowserTest::TearDownOnMainThread();
+    extensions::ExtensionBrowserTest::TearDownOnMainThread();
   }
 
   // Opens the app menu and the context menu of the overflowed action, and
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
index 836a0a41..3cc5324 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -360,9 +360,7 @@
 }
 
 - (gfx::Size)preferredSize {
-  gfx::Size size = toolbarActionsBar_->GetFullSize();
-  size.Enlarge(kBrowserActionHorizontalPadding * 2, 0);
-  return size;
+  return toolbarActionsBar_->GetFullSize();
 }
 
 - (NSPoint)popupPointForId:(const std::string&)id {
diff --git a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac.mm b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac.mm
index 722b757..60c6b53 100644
--- a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac.mm
+++ b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac.mm
@@ -23,7 +23,6 @@
 #import "ui/base/cocoa/menu_controller.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/strings/grit/ui_strings.h"
 
@@ -197,42 +196,24 @@
 }
 
 void RenderViewContextMenuMac::ExecuteCommand(int command_id, int event_flags) {
-  switch (command_id) {
-    case IDC_CONTENT_CONTEXT_EMOJI:
-      [NSApp orderFrontCharacterPalette:nil];
-      RenderViewContextMenu::RecordUsedItem(command_id);
-      break;
-
-    case IDC_CONTENT_CONTEXT_LOOK_UP:
-      LookUpInDictionary();
-      break;
-
-    default:
-      RenderViewContextMenu::ExecuteCommand(command_id, event_flags);
-      break;
-  }
+  if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
+    LookUpInDictionary();
+  else
+    RenderViewContextMenu::ExecuteCommand(command_id, event_flags);
 }
 
 bool RenderViewContextMenuMac::IsCommandIdChecked(int command_id) const {
-  if (command_id == IDC_CONTENT_CONTEXT_EMOJI ||
-      command_id == IDC_CONTENT_CONTEXT_LOOK_UP) {
+  if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
     return false;
-  }
 
   return RenderViewContextMenu::IsCommandIdChecked(command_id);
 }
 
 bool RenderViewContextMenuMac::IsCommandIdEnabled(int command_id) const {
-  switch (command_id) {
-    case IDC_CONTENT_CONTEXT_EMOJI:
-      return params_.is_editable;
+  if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
+    return true;
 
-    case IDC_CONTENT_CONTEXT_LOOK_UP:
-      return true;
-
-    default:
-      return RenderViewContextMenu::IsCommandIdEnabled(command_id);
-  }
+  return RenderViewContextMenu::IsCommandIdEnabled(command_id);
 }
 
 void RenderViewContextMenuMac::Show() {
@@ -320,16 +301,17 @@
 }
 
 void RenderViewContextMenuMac::InitToolkitMenu() {
-  if (params_.input_field_type ==
-      blink::WebContextMenuData::kInputFieldTypePassword)
+  if (params_.selection_text.empty() ||
+      params_.input_field_type ==
+          blink::WebContextMenuData::kInputFieldTypePassword)
     return;
 
-  int index = 0;
-  if (!params_.selection_text.empty() && params_.link_url.is_empty()) {
+  if (params_.link_url.is_empty()) {
     // In case the user has selected a word that triggers spelling suggestions,
     // show the dictionary lookup under the group that contains the command to
     // “Add to Dictionary.”
-    index = menu_model_.GetIndexOfCommandId(IDC_SPELLCHECK_ADD_TO_DICTIONARY);
+    int index =
+        menu_model_.GetIndexOfCommandId(IDC_SPELLCHECK_ADD_TO_DICTIONARY);
     if (index < 0) {
       index = 0;
     } else {
@@ -348,17 +330,6 @@
     menu_model_.InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
   }
 
-  // The Emoji menu item is available for editable text fields, unless the
-  // selected text is a misspelling.
-  if (params_.is_editable && params_.misspelled_word.empty() &&
-      base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu)) {
-    // The "Emoji" item is available near the top of the context menu, after
-    // any "Look Up" of selected text.
-    menu_model_.InsertItemWithStringIdAt(index++, IDC_CONTENT_CONTEXT_EMOJI,
-                                         IDS_CONTENT_CONTEXT_EMOJI);
-    menu_model_.InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
-  }
-
   text_services_context_menu_.AppendToContextMenu(&menu_model_);
 }
 
diff --git a/chrome/browser/ui/cocoa/tabbed_browser_window.mm b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
index 864789d9..38163a1 100644
--- a/chrome/browser/ui/cocoa/tabbed_browser_window.mm
+++ b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
@@ -23,9 +23,6 @@
 // window buttons (zoom, close, miniaturize).
 constexpr NSInteger kWindowButtonsOffsetFromBottom = 9;
 constexpr NSInteger kWindowButtonsOffsetFromLeft = 11;
-
-// Offset from the top/right of the window to the Mavericks full screen button.
-constexpr NSInteger kFullScreenButtonOffset = 9;
 }  // namespace
 
 @interface TabbedBrowserWindow ()
@@ -115,30 +112,6 @@
 
 @end
 
-@interface MavericksTabbedBrowserWindowFrame : FullSizeTabbedBrowserWindowFrame
-@end
-
-@implementation MavericksTabbedBrowserWindowFrame
-
-// 10.10+ has no separate fullscreen button.
-- (NSPoint)_fullScreenButtonOrigin
-    __attribute__((availability(macos, obsoleted = 10.10))) {
-  CGFloat xAdjustment = [static_cast<TabbedBrowserWindow*>(self.window)
-      fullScreenButtonOriginAdjustment];
-  NSSize fullScreenButtonSize = [self fullScreenButton].frame.size;
-  return NSMakePoint(NSMaxX(self.bounds) - fullScreenButtonSize.width -
-                         kFullScreenButtonOffset - xAdjustment,
-                     NSMaxY(self.bounds) - fullScreenButtonSize.height -
-                         kFullScreenButtonOffset);
-}
-
-// 10.10+ adds the content view below the window controls by default.
-- (void)_setContentView:(NSView*)contentView {
-  [self addSubview:contentView positioned:NSWindowBelow relativeTo:nil];
-}
-
-@end
-
 @implementation TabbedBrowserWindow
 
 // FramedBrowserWindow overrides.
@@ -158,12 +131,9 @@
   // Because NSThemeFrame is imported weakly, if it's not present at runtime
   // then it and its subclasses will be nil.
   if ([TabbedBrowserWindowFrame class]) {
-    if (@available(macOS 10.10, *)) {
-      return chrome::ShouldUseFullSizeContentView()
-                 ? [TabbedBrowserWindowFrame class]
-                 : [FullSizeTabbedBrowserWindowFrame class];
-    }
-    return [MavericksTabbedBrowserWindowFrame class];
+    return chrome::ShouldUseFullSizeContentView()
+               ? [TabbedBrowserWindowFrame class]
+               : [FullSizeTabbedBrowserWindowFrame class];
   }
   return [super frameViewClassForStyleMask:windowStyle];
 }
diff --git a/chrome/browser/ui/extensions/blocked_action_bubble_browsertest.cc b/chrome/browser/ui/extensions/blocked_action_bubble_browsertest.cc
index b2fb954..3ed7091 100644
--- a/chrome/browser/ui/extensions/blocked_action_bubble_browsertest.cc
+++ b/chrome/browser/ui/extensions/blocked_action_bubble_browsertest.cc
@@ -20,7 +20,7 @@
 #include "net/dns/mock_host_resolver.h"
 
 class ExtensionBlockedActionsBubbleTest
-    : public SupportsTestDialog<ExtensionBrowserTest> {
+    : public SupportsTestDialog<extensions::ExtensionBrowserTest> {
  public:
   ExtensionBlockedActionsBubbleTest();
   ~ExtensionBlockedActionsBubbleTest() override;
@@ -45,13 +45,13 @@
 
 void ExtensionBlockedActionsBubbleTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   scoped_feature_list_.InitAndEnableFeature(
       extensions::features::kRuntimeHostPermissions);
 }
 
 void ExtensionBlockedActionsBubbleTest::SetUpOnMainThread() {
-  ExtensionBrowserTest::SetUpOnMainThread();
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
   host_resolver()->AddRule("*", "127.0.0.1");
 }
 
diff --git a/chrome/browser/ui/extensions/extension_enable_flow_browsertest.cc b/chrome/browser/ui/extensions/extension_enable_flow_browsertest.cc
index 42604d74..e0a076cd 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow_browsertest.cc
+++ b/chrome/browser/ui/extensions/extension_enable_flow_browsertest.cc
@@ -74,7 +74,7 @@
 
 }  // namespace
 
-using ExtensionEnableFlowBrowserTest = ExtensionBrowserTest;
+using ExtensionEnableFlowBrowserTest = extensions::ExtensionBrowserTest;
 
 // Test that trying to enable an extension that's blocked by policy fails
 // gracefully. See https://crbug.com/783831.
diff --git a/chrome/browser/ui/extensions/extension_installed_bubble_browsertest.cc b/chrome/browser/ui/extensions/extension_installed_bubble_browsertest.cc
index 7d0ccb65..bd39fd9 100644
--- a/chrome/browser/ui/extensions/extension_installed_bubble_browsertest.cc
+++ b/chrome/browser/ui/extensions/extension_installed_bubble_browsertest.cc
@@ -29,7 +29,7 @@
 using ActionType = extensions::ExtensionBuilder::ActionType;
 
 class ExtensionInstalledBubbleBrowserTest
-    : public SupportsTestDialog<ExtensionBrowserTest> {
+    : public SupportsTestDialog<extensions::ExtensionBrowserTest> {
  public:
   ExtensionInstalledBubbleBrowserTest()
       : disable_animations_(&ToolbarActionsBar::disable_animations_for_testing_,
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 302d1b2b..5cef54e 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -211,7 +211,7 @@
 // is a Hosted or Bookmark app. |desktop_pwa_flag| enables the
 // kDesktopPWAWindowing flag.
 class HostedAppTest
-    : public ExtensionBrowserTest,
+    : public extensions::ExtensionBrowserTest,
       public ::testing::WithParamInterface<std::tuple<AppType, bool>> {
  public:
   HostedAppTest()
@@ -239,7 +239,7 @@
 #endif
     }
 
-    ExtensionBrowserTest::SetUp();
+    extensions::ExtensionBrowserTest::SetUp();
   }
 
  protected:
@@ -299,24 +299,24 @@
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
     ProfileIOData::SetCertVerifierForTesting(&mock_cert_verifier_);
   }
 
   void TearDownInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
     ProfileIOData::SetCertVerifierForTesting(nullptr);
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     // Browser will both run and display insecure content.
     command_line->AppendSwitch(switches::kAllowRunningInsecureContent);
     command_line->AppendSwitch(switches::kUseMockCertVerifierForTesting);
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
     // By default, all SSL cert checks are valid. Can be overriden in tests.
     cert_verifier_.set_default_result(net::OK);
diff --git a/chrome/browser/ui/interventions/framebust_block_message_delegate.cc b/chrome/browser/ui/interventions/framebust_block_message_delegate.cc
index 32b6d4e..d818ec1 100644
--- a/chrome/browser/ui/interventions/framebust_block_message_delegate.cc
+++ b/chrome/browser/ui/interventions/framebust_block_message_delegate.cc
@@ -7,10 +7,16 @@
 #include <memory>
 #include <utility>
 
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
 
 FramebustBlockMessageDelegate::FramebustBlockMessageDelegate(
     content::WebContents* web_contents,
@@ -40,3 +46,14 @@
       blocked_url_, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
       ui::PAGE_TRANSITION_LINK, false));
 }
+
+void FramebustBlockMessageDelegate::DeclineInterventionSticky() {
+  HostContentSettingsMap* settings_map =
+      HostContentSettingsMapFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+  DCHECK(settings_map);
+  settings_map->SetContentSettingDefaultScope(
+      web_contents_->GetLastCommittedURL(), GURL(),
+      CONTENT_SETTINGS_TYPE_POPUPS, std::string(), CONTENT_SETTING_ALLOW);
+  DeclineIntervention();
+}
diff --git a/chrome/browser/ui/interventions/framebust_block_message_delegate.h b/chrome/browser/ui/interventions/framebust_block_message_delegate.h
index 25bb94e..b15b29a 100644
--- a/chrome/browser/ui/interventions/framebust_block_message_delegate.h
+++ b/chrome/browser/ui/interventions/framebust_block_message_delegate.h
@@ -39,6 +39,7 @@
   // InterventionDelegate:
   void AcceptIntervention() override;
   void DeclineIntervention() override;
+  void DeclineInterventionSticky() override;
 
  private:
   // Callback to be called when the user performs an action regarding this
diff --git a/chrome/browser/ui/interventions/intervention_delegate.h b/chrome/browser/ui/interventions/intervention_delegate.h
index fddc5bc..234c596 100644
--- a/chrome/browser/ui/interventions/intervention_delegate.h
+++ b/chrome/browser/ui/interventions/intervention_delegate.h
@@ -11,6 +11,10 @@
   virtual void AcceptIntervention() = 0;
   virtual void DeclineIntervention() = 0;
 
+  // Called if the user declines the intervention in a sticky way. e.g. by
+  // indicating they always want to decline the intervention on the site.
+  virtual void DeclineInterventionSticky() = 0;
+
  protected:
   virtual ~InterventionDelegate() = default;
 };
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
index 5ad8a7f..840112ad 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
@@ -133,6 +133,9 @@
             "GtkMenu#menu GtkSeparator#separator.horizontal");
       }
       return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem.separator");
+    case ui::NativeTheme::kColorId_TouchableMenuItemLabelColor:
+    case ui::NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor:
+      return kInvalidColorIdColor;
 
     // Label
     case ui::NativeTheme::kColorId_LabelEnabledColor:
diff --git a/chrome/browser/ui/location_bar/location_bar_browsertest.cc b/chrome/browser/ui/location_bar/location_bar_browsertest.cc
index 302848b..b0a24b8 100644
--- a/chrome/browser/ui/location_bar/location_bar_browsertest.cc
+++ b/chrome/browser/ui/location_bar/location_bar_browsertest.cc
@@ -16,7 +16,7 @@
 #include "extensions/common/feature_switch.h"
 #include "extensions/common/value_builder.h"
 
-class LocationBarBrowserTest : public ExtensionBrowserTest {
+class LocationBarBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   LocationBarBrowserTest() {}
   ~LocationBarBrowserTest() override {}
@@ -31,7 +31,7 @@
 };
 
 void LocationBarBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
 
   // In order to let a vanilla extension override the bookmark star, we have to
   // enable the switch.
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
index 5c42a15..0603ce2 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -14,9 +14,9 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service.h"
-#include "chrome/browser/ui/omnibox/favicon_cache.h"
 #include "chrome/common/search/instant_types.h"
 #include "components/favicon_base/favicon_types.h"
+#include "components/omnibox/browser/favicon_cache.h"
 #include "components/omnibox/browser/omnibox_client.h"
 
 class ChromeOmniboxEditController;
diff --git a/chrome/browser/ui/omnibox/omnibox_theme.cc b/chrome/browser/ui/omnibox/omnibox_theme.cc
index 69fcd07..cb99057b6 100644
--- a/chrome/browser/ui/omnibox/omnibox_theme.cc
+++ b/chrome/browser/ui/omnibox/omnibox_theme.cc
@@ -190,7 +190,7 @@
 
   switch (state) {
     case OmniboxPartState::CHIP_DEFAULT:
-      return gfx::kGoogleGrey800;
+      return gfx::kChromeIconGrey;
     case OmniboxPartState::CHIP_SECURE:
       return gfx::kGoogleGreen600;
     case OmniboxPartState::CHIP_DANGEROUS:
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index 4eb66e24..77c5011 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -112,7 +112,13 @@
     {CONTENT_SETTINGS_TYPE_COOKIES, 0},
     {CONTENT_SETTINGS_TYPE_IMAGES, IDS_PAGE_INFO_TYPE_IMAGES},
     {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_PAGE_INFO_TYPE_JAVASCRIPT},
+#if defined(OS_ANDROID)
+    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_PAGE_INFO_TYPE_POPUPS_REDIRECTS},
+#else
+    // TODO(csharrison,ericrobinson): Use IDS_PAGE_INFO_TYPE_POPUPS_REDIRECTS on
+    // desktop as well.
     {CONTENT_SETTINGS_TYPE_POPUPS, IDS_PAGE_INFO_TYPE_POPUPS},
+#endif
 #if BUILDFLAG(ENABLE_PLUGINS)
     {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_PAGE_INFO_TYPE_FLASH},
 #endif
diff --git a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
index 3f36d3a..df208dad 100644
--- a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.h
@@ -43,7 +43,7 @@
 // Use this class to test on a default window or an app window. Inheriting from
 // ExtensionBrowserTest allows us to easily load and launch apps, and doesn't
 // really add any extra work.
-class PermissionBubbleBrowserTest : public ExtensionBrowserTest {
+class PermissionBubbleBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   PermissionBubbleBrowserTest();
   ~PermissionBubbleBrowserTest() override;
diff --git a/chrome/browser/ui/search/instant_theme_browsertest.cc b/chrome/browser/ui/search/instant_theme_browsertest.cc
index d045d5e3..7f55eb9 100644
--- a/chrome/browser/ui/search/instant_theme_browsertest.cc
+++ b/chrome/browser/ui/search/instant_theme_browsertest.cc
@@ -24,7 +24,8 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class InstantThemeTest : public ExtensionBrowserTest, public InstantTestBase {
+class InstantThemeTest : public extensions::ExtensionBrowserTest,
+                         public InstantTestBase {
  public:
   InstantThemeTest() {}
 
@@ -37,7 +38,7 @@
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
     content::URLDataSource::Add(profile(), new ThemeSource(profile()));
   }
@@ -58,7 +59,7 @@
         content::Source<ThemeService>(
             ThemeServiceFactory::GetForProfile(profile())));
     ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(
-        theme_path, 1, ExtensionBrowserTest::browser()));
+        theme_path, 1, extensions::ExtensionBrowserTest::browser()));
     theme_change_observer.Wait();
     size_t num_after = extensions::ExtensionRegistry::Get(profile())
                            ->enabled_extensions()
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 87c14a4..c2fa7d20 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -152,16 +152,16 @@
 
 }  // namespace
 
-class StartupBrowserCreatorTest : public ExtensionBrowserTest {
+class StartupBrowserCreatorTest : public extensions::ExtensionBrowserTest {
  protected:
   StartupBrowserCreatorTest() {}
 
   bool SetUpUserDataDirectory() override {
-    return ExtensionBrowserTest::SetUpUserDataDirectory();
+    return extensions::ExtensionBrowserTest::SetUpUserDataDirectory();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(switches::kHomePage, url::kAboutBlankURL);
 #if defined(OS_CHROMEOS)
     // TODO(nkostylev): Investigate if we can remove this switch.
diff --git a/chrome/browser/ui/tabs/tab_ukm_test_helper.cc b/chrome/browser/ui/tabs/tab_ukm_test_helper.cc
index b8e590b..e323235 100644
--- a/chrome/browser/ui/tabs/tab_ukm_test_helper.cc
+++ b/chrome/browser/ui/tabs/tab_ukm_test_helper.cc
@@ -81,8 +81,8 @@
         ukm_recorder_.GetEntriesByName(entry_name)[first_unexpected_index];
 
     std::ostringstream entry_metrics;
-    for (const ukm::mojom::UkmMetricPtr& metric : ukm_entry->metrics)
-      entry_metrics << "\n" << metric->metric_hash << ": " << metric->value;
+    for (const auto& metric : ukm_entry->metrics)
+      entry_metrics << "\n" << metric.first << ": " << metric.second;
     LOG(ERROR) << "First unexpected entry: " << entry_metrics.str();
   }
 }
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
index 97e7856e..3e00fdc 100644
--- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
+++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
@@ -67,19 +67,19 @@
 
 void BrowserActionsBarBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   ToolbarActionsBar::disable_animations_for_testing_ = true;
 }
 
 void BrowserActionsBarBrowserTest::SetUpOnMainThread() {
-  ExtensionBrowserTest::SetUpOnMainThread();
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
   browser_actions_bar_ = BrowserActionTestUtil::Create(browser());
   toolbar_model_ = ToolbarActionsModel::Get(profile());
 }
 
 void BrowserActionsBarBrowserTest::TearDownOnMainThread() {
   ToolbarActionsBar::disable_animations_for_testing_ = false;
-  ExtensionBrowserTest::TearDownOnMainThread();
+  extensions::ExtensionBrowserTest::TearDownOnMainThread();
 }
 
 void BrowserActionsBarBrowserTest::LoadExtensions() {
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
index 08fe6fb..4a8690a9 100644
--- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
+++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
@@ -19,7 +19,7 @@
 class ToolbarActionsModel;
 
 // A platform-independent browser test class for the browser actions bar.
-class BrowserActionsBarBrowserTest : public ExtensionBrowserTest {
+class BrowserActionsBarBrowserTest : public extensions::ExtensionBrowserTest {
  protected:
   BrowserActionsBarBrowserTest();
   ~BrowserActionsBarBrowserTest() override;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index c9e15e17..e9f2e213 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -188,6 +188,9 @@
       ->mus_properties[ui::mojom::WindowManager::kShelfItemType_Property] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           static_cast<int64_t>(ash::TYPE_APP));
+  init_params
+      ->mus_properties[ui::mojom::WindowManager::kWindowTitleShown_Property] =
+      mojo::ConvertTo<std::vector<uint8_t>>(static_cast<int64_t>(false));
 }
 
 void ChromeNativeAppWindowViewsAuraAsh::OnBeforePanelWidgetInit(
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 16f8c78..1aa3804 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -4,146 +4,65 @@
 
 #include "chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h"
 
+#include <algorithm>
+
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
 #include "chrome/browser/ui/autofill/autofill_popup_layout_model.h"
+#include "chrome/browser/ui/views/harmony/chrome_typography.h"
+#include "chrome/browser/ui/views/harmony/harmony_typography_provider.h"
 #include "components/autofill/core/browser/popup_item_ids.h"
 #include "components/autofill/core/browser/suggestion.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/font.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/style/typography.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-#include <algorithm>
-
 namespace {
 
-// Padding values for the entire dropdown.
-const int kPopupTopBottomPadding = 8;
-const int kPopupSidePadding = 0;
-
-// Padding values and dimensions for rows.
-const int kRowHeight = 28;
-const int kRowTopBottomPadding = 0;
-// Used for padding on sides of rows, as well as minimum spacing between
-// items inside of rows.
-const int kRowHorizontalSpacing = 16;
-
-// Padding values specific to the separator row.
-const int kSeparatorTopBottomPadding = 4;
-const int kSeparatorSidePadding = 0;
-
 // By spec, dropdowns should have a min width of 64, and should always have
-// a width which is a multiple of 16.
-const int kDropdownWidthMultiple = 16;
+// a width which is a multiple of 12.
+const int kDropdownWidthMultiple = 12;
 const int kDropdownMinWidth = 64;
 
+// TODO(crbug.com/768881): Determine how colors should be shared with menus
+// and/or omnibox, and how these should interact (if at all) with native
+// theme colors.
+const SkColor kBackgroundColor = SK_ColorWHITE;
+const SkColor kSelectedBackgroundColor = gfx::kGoogleGrey200;
+const SkColor kFooterBackgroundColor = gfx::kGoogleGrey050;
+const SkColor kSeparatorColor = gfx::kGoogleGrey200;
+
 }  // namespace
 
 namespace autofill {
 
-AutofillPopupRowView::AutofillPopupRowView(AutofillPopupController* controller,
-                                           int line_number)
-    : controller_(controller), line_number_(line_number) {
-  int frontend_id = controller_->GetSuggestionAt(line_number_).frontend_id;
-  is_separator_ = frontend_id == autofill::POPUP_ITEM_ID_SEPARATOR;
-  is_warning_ =
-      frontend_id ==
-          autofill::POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
+namespace {
 
-  SetFocusBehavior(is_separator_ ? FocusBehavior::NEVER
-                                 : FocusBehavior::ALWAYS);
-  CreateContent();
-}
+// This represents a single selectable item. Subclasses distinguish between
+// footer and suggestion rows, which are structurally similar but have
+// distinct styling.
+class AutofillPopupItemView : public AutofillPopupRowView {
+ public:
+  ~AutofillPopupItemView() override = default;
 
-void AutofillPopupRowView::AcceptSelection() {
-  controller_->AcceptSuggestion(line_number_);
-}
+  // views::View:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
+    node_data->SetName(controller_->GetSuggestionAt(line_number_).value);
 
-void AutofillPopupRowView::SetSelected(bool is_selected) {
-  if (is_selected == is_selected_)
-    return;
-
-  is_selected_ = is_selected;
-  NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
-  RefreshStyle();
-}
-
-void AutofillPopupRowView::RefreshStyle() {
-  // Only content rows, not separators, can be highlighted/selected.
-  DCHECK(!is_separator_);
-
-  SetBackground(views::CreateThemedSolidBackground(
-      this, is_selected_
-                ? ui::NativeTheme::kColorId_ResultsTableSelectedBackground
-                : ui::NativeTheme::kColorId_ResultsTableNormalBackground));
-
-  if (text_label_) {
-    text_label_->SetEnabledColor(is_selected_ ? text_selected_color_
-                                              : text_color_);
-  }
-
-  if (subtext_label_) {
-    subtext_label_->SetEnabledColor(is_selected_ ? subtext_selected_color_
-                                                 : subtext_color_);
-  }
-}
-
-void AutofillPopupRowView::OnMouseEntered(const ui::MouseEvent& event) {
-  controller_->SetSelectedLine(line_number_);
-}
-
-void AutofillPopupRowView::OnMouseReleased(const ui::MouseEvent& event) {
-  // Clicking a separator should not trigger any events, so we return early.
-  if (is_separator_)
-    return;
-
-  if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
-    AcceptSelection();
-}
-
-bool AutofillPopupRowView::OnMouseDragged(const ui::MouseEvent& event) {
-  return true;
-}
-
-bool AutofillPopupRowView::OnMousePressed(const ui::MouseEvent& event) {
-  return true;
-}
-
-void AutofillPopupRowView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
-  if (is_separator_)
-    return;
-
-  text_color_ = theme->GetSystemColor(
-      is_warning_ ? ui::NativeTheme::kColorId_ResultsTableNegativeText
-                  : ui::NativeTheme::kColorId_ResultsTableNormalText);
-  text_selected_color_ = theme->GetSystemColor(
-      is_warning_ ? ui::NativeTheme::kColorId_ResultsTableNegativeSelectedText
-                  : ui::NativeTheme::kColorId_ResultsTableSelectedText);
-
-  subtext_color_ = theme->GetSystemColor(
-      ui::NativeTheme::kColorId_ResultsTableNormalDimmedText);
-  subtext_selected_color_ = theme->GetSystemColor(
-      ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText);
-
-  RefreshStyle();
-}
-
-void AutofillPopupRowView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->SetName(controller_->GetSuggestionAt(line_number_).value);
-
-  if (is_separator_) {
-    // Separators are not selectable.
-    node_data->role = ax::mojom::Role::kSplitter;
-  } else {
     // Options are selectable.
     node_data->role = ax::mojom::Role::kMenuItem;
     node_data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
@@ -164,60 +83,249 @@
     node_data->AddIntAttribute(ax::mojom::IntAttribute::kSetSize, set_size);
     node_data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, pos_in_set);
   }
+
+  void OnMouseEntered(const ui::MouseEvent& event) override {
+    controller_->SetSelectedLine(line_number_);
+  }
+
+  void OnMouseReleased(const ui::MouseEvent& event) override {
+    if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
+      controller_->AcceptSuggestion(line_number_);
+  }
+
+ protected:
+  AutofillPopupItemView(AutofillPopupController* controller, int line_number)
+      : AutofillPopupRowView(controller, line_number) {}
+
+  // AutofillPopupRowView:
+  void CreateContent() override {
+    auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::kHorizontal,
+        gfx::Insets(0, views::MenuConfig::instance().item_left_margin)));
+
+    layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
+    layout->set_minimum_cross_axis_size(
+        views::MenuConfig::instance().touchable_menu_height);
+
+    // TODO(crbug.com/831603): Remove elision responsibilities from controller.
+    text_label_ = new views::Label(
+        controller_->GetElidedValueAt(line_number_),
+        {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+                               views::style::TextStyle::STYLE_PRIMARY)});
+
+    AddChildView(text_label_);
+
+    auto* spacer = new views::View;
+    spacer->SetPreferredSize(gfx::Size(
+        views::MenuConfig::instance().label_to_minor_text_padding, 1));
+    AddChildView(spacer);
+    layout->SetFlexForView(spacer, /*flex*/ 1);
+
+    const base::string16& description_text =
+        controller_->GetElidedLabelAt(line_number_);
+    if (!description_text.empty()) {
+      subtext_label_ = new views::Label(
+          description_text,
+          {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+                                 ChromeTextStyle::STYLE_HINT)});
+
+      AddChildView(subtext_label_);
+    }
+
+    const gfx::ImageSkia icon =
+        controller_->layout_model().GetIconImage(line_number_);
+    if (!icon.isNull()) {
+      auto* image_view = new views::ImageView();
+      image_view->SetImage(icon);
+
+      image_view->SetBorder(views::CreateEmptyBorder(
+          0, views::MenuConfig::instance().icon_to_label_padding, 0, 0));
+      AddChildView(image_view);
+    }
+  }
+
+  void RefreshStyle() override {
+    SetBackground(CreateBackground());
+
+    if (text_label_) {
+      int text_style = is_warning_ ? ChromeTextStyle::STYLE_RED
+                                   : views::style::TextStyle::STYLE_PRIMARY;
+
+      text_label_->SetEnabledColor(views::style::GetColor(
+          *this, ChromeTextContext::CONTEXT_BODY_TEXT_LARGE, text_style));
+    }
+
+    if (subtext_label_) {
+      SkColor secondary = views::style::GetColor(
+          *this, ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+          ChromeTextStyle::STYLE_HINT);
+      subtext_label_->SetEnabledColor(secondary);
+    }
+
+    SchedulePaint();
+  }
+
+ private:
+  views::Label* text_label_ = nullptr;
+  views::Label* subtext_label_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopupItemView);
+};
+
+class AutofillPopupSuggestionView : public AutofillPopupItemView {
+ public:
+  ~AutofillPopupSuggestionView() override = default;
+
+  static AutofillPopupSuggestionView* Create(
+      AutofillPopupController* controller,
+      int line_number) {
+    AutofillPopupSuggestionView* result =
+        new AutofillPopupSuggestionView(controller, line_number);
+    result->Init();
+    return result;
+  }
+
+ protected:
+  // AutofillPopupRowView:
+  std::unique_ptr<views::Background> CreateBackground() override {
+    return views::CreateSolidBackground(is_selected_ ? kSelectedBackgroundColor
+                                                     : kBackgroundColor);
+  }
+
+ private:
+  AutofillPopupSuggestionView(AutofillPopupController* controller,
+                              int line_number)
+      : AutofillPopupItemView(controller, line_number) {
+    SetFocusBehavior(FocusBehavior::ALWAYS);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopupSuggestionView);
+};
+
+class AutofillPopupFooterView : public AutofillPopupItemView {
+ public:
+  ~AutofillPopupFooterView() override = default;
+
+  static AutofillPopupFooterView* Create(AutofillPopupController* controller,
+                                         int line_number) {
+    AutofillPopupFooterView* result =
+        new AutofillPopupFooterView(controller, line_number);
+    result->Init();
+    return result;
+  }
+
+ protected:
+  // AutofillPopupRowView:
+  std::unique_ptr<views::Background> CreateBackground() override {
+    return views::CreateSolidBackground(is_selected_ ? kSelectedBackgroundColor
+                                                     : kFooterBackgroundColor);
+  }
+
+ private:
+  AutofillPopupFooterView(AutofillPopupController* controller, int line_number)
+      : AutofillPopupItemView(controller, line_number) {
+    SetFocusBehavior(FocusBehavior::ALWAYS);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopupFooterView);
+};
+
+class AutofillPopupSeparatorView : public AutofillPopupRowView {
+ public:
+  ~AutofillPopupSeparatorView() override = default;
+
+  static AutofillPopupSeparatorView* Create(AutofillPopupController* controller,
+                                            int line_number) {
+    AutofillPopupSeparatorView* result =
+        new AutofillPopupSeparatorView(controller, line_number);
+    result->Init();
+    return result;
+  }
+
+  // views::View:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
+    // Separators are not selectable.
+    node_data->role = ax::mojom::Role::kSplitter;
+  }
+
+  void OnMouseEntered(const ui::MouseEvent& event) override {}
+  void OnMouseReleased(const ui::MouseEvent& event) override {}
+  void OnNativeThemeChanged(const ui::NativeTheme* theme) override {
+    RefreshStyle();
+  }
+
+ protected:
+  // AutofillPopupRowView:
+  void CreateContent() override {
+    SetLayoutManager(std::make_unique<views::FillLayout>());
+
+    // In order to draw the horizontal line for the separator, create an empty
+    // view which matches the width of the parent (by using full flex) and has a
+    // height equal to the desired padding, then paint a border on the bottom.
+    empty_ = new views::View();
+    empty_->SetPreferredSize(
+        gfx::Size(1, views::MenuConfig::instance().menu_vertical_border_size));
+    AddChildView(empty_);
+  }
+
+  void RefreshStyle() override {
+    empty_->SetBorder(views::CreateSolidSidedBorder(
+        /*top=*/0,
+        /*left=*/0,
+        /*bottom=*/views::MenuConfig::instance().separator_thickness,
+        /*right=*/0,
+        /*color=*/kSeparatorColor));
+    SchedulePaint();
+  }
+
+  std::unique_ptr<views::Background> CreateBackground() override {
+    return views::CreateSolidBackground(SK_ColorWHITE);
+  }
+
+ private:
+  AutofillPopupSeparatorView(AutofillPopupController* controller,
+                             int line_number)
+      : AutofillPopupRowView(controller, line_number) {
+    SetFocusBehavior(FocusBehavior::NEVER);
+  }
+
+  views::View* empty_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopupSeparatorView);
+};
+
+}  // namespace
+
+void AutofillPopupRowView::SetSelected(bool is_selected) {
+  if (is_selected == is_selected_)
+    return;
+
+  is_selected_ = is_selected;
+  NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
+  RefreshStyle();
 }
 
-void AutofillPopupRowView::CreateContent() {
-  if (is_separator_) {
-    views::BoxLayout* layout =
-        SetLayoutManager(std::make_unique<views::BoxLayout>(
-            views::BoxLayout::kVertical,
-            gfx::Insets(kSeparatorTopBottomPadding, kSeparatorSidePadding)));
-    layout->set_main_axis_alignment(
-        views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
-    AddChildView(new views::Separator());
-    return;
-  }
+bool AutofillPopupRowView::OnMouseDragged(const ui::MouseEvent& event) {
+  return true;
+}
 
-  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal,
-      gfx::Insets(kRowTopBottomPadding, kRowHorizontalSpacing)));
+bool AutofillPopupRowView::OnMousePressed(const ui::MouseEvent& event) {
+  return true;
+}
 
-  layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
-  layout->set_minimum_cross_axis_size(kRowHeight);
+AutofillPopupRowView::AutofillPopupRowView(AutofillPopupController* controller,
+                                           int line_number)
+    : controller_(controller), line_number_(line_number) {
+  int frontend_id = controller_->GetSuggestionAt(line_number_).frontend_id;
+  is_warning_ =
+      frontend_id ==
+      autofill::POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
+}
 
-  const gfx::ImageSkia icon =
-      controller_->layout_model().GetIconImage(line_number_);
-  if (!icon.isNull()) {
-    auto* image_view = new views::ImageView();
-    image_view->SetImage(icon);
-    image_view->SetBorder(
-        views::CreateEmptyBorder(0, 0, 0, kRowHorizontalSpacing / 2));
-    AddChildView(image_view);
-  }
-
-  // TODO(tmartino): Remove elision, font list, and font color
-  // responsibilities from controller.
-  text_label_ = new views::Label(
-      controller_->GetElidedValueAt(line_number_),
-      {controller_->layout_model().GetValueFontListForRow(line_number_)});
-
-  AddChildView(text_label_);
-
-  auto* spacer = new views::View;
-  spacer->SetPreferredSize(gfx::Size(kRowHorizontalSpacing, 1));
-  AddChildView(spacer);
-  layout->SetFlexForView(spacer, /*flex*/ 1);
-
-  const base::string16& description_text =
-      controller_->GetElidedLabelAt(line_number_);
-  if (!description_text.empty()) {
-    subtext_label_ = new views::Label(
-        description_text,
-        {controller_->layout_model().GetLabelFontListForRow(line_number_)});
-
-    AddChildView(subtext_label_);
-  }
+void AutofillPopupRowView::Init() {
+  CreateContent();
+  RefreshStyle();
 }
 
 AutofillPopupViewNativeViews::AutofillPopupViewNativeViews(
@@ -225,15 +333,17 @@
     views::Widget* parent_widget)
     : AutofillPopupBaseView(controller, parent_widget),
       controller_(controller) {
+  // TODO(crbug.com/768881): kPopupTopBottomPadding is not needed on the bottom
+  // when a footer is present.
   views::BoxLayout* layout =
       SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::kVertical,
-          gfx::Insets(kPopupTopBottomPadding, kPopupSidePadding)));
+          gfx::Insets(views::MenuConfig::instance().menu_vertical_border_size,
+                      0)));
   layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
 
   CreateChildViews();
-  SetBackground(views::CreateThemedSolidBackground(
-      this, ui::NativeTheme::kColorId_ResultsTableNormalBackground));
+  SetBackground(views::CreateSolidBackground(kBackgroundColor));
 }
 
 AutofillPopupViewNativeViews::~AutofillPopupViewNativeViews() {}
@@ -297,7 +407,17 @@
   RemoveAllChildViews(true /* delete_children */);
   rows_.clear();
   for (int i = 0; i < controller_->GetLineCount(); ++i) {
-    rows_.push_back(new AutofillPopupRowView(controller_, i));
+    switch (controller_->GetSuggestionAt(i).frontend_id) {
+      case autofill::PopupItemId::POPUP_ITEM_ID_SEPARATOR:
+        rows_.push_back(AutofillPopupSeparatorView::Create(controller_, i));
+        break;
+      case autofill::PopupItemId::POPUP_ITEM_ID_CLEAR_FORM:
+      case autofill::PopupItemId::POPUP_ITEM_ID_AUTOFILL_OPTIONS:
+        rows_.push_back(AutofillPopupFooterView::Create(controller_, i));
+        break;
+      default:
+        rows_.push_back(AutofillPopupSuggestionView::Create(controller_, i));
+    }
     AddChildView(rows_.back());
   }
 }
@@ -305,11 +425,11 @@
 void AutofillPopupViewNativeViews::DoUpdateBoundsAndRedrawPopup() {
   SizeToPreferredSize();
 
-  // TODO(tmartino): Currently, we rely on the delegate bounds to provide the
-  // origin. The size included in these bounds, however, is not relevant since
-  // this Views-based implementation can provide its own appropriate width and
-  // height. In the future, we should be able to drop the size calculation
-  // logic from the delegate.
+  // TODO(crbug.com/831603): Currently, we rely on the delegate bounds to
+  // provide the origin. The size included in these bounds, however, is not
+  // relevant since this Views-based implementation can provide its own
+  // appropriate width and height. In the future, we should be able to drop the
+  // size calculation logic from the delegate.
   gfx::Rect bounds(delegate()->popup_bounds().origin(), size());
 
   // The Widget's bounds need to account for the border.
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
index 49f4c55..e3e07b25 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -9,60 +9,47 @@
 #include "base/optional.h"
 #include "chrome/browser/ui/autofill/autofill_popup_view.h"
 #include "chrome/browser/ui/views/autofill/autofill_popup_base_view.h"
-#include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/font_list.h"
 
+#include <memory>
 #include <vector>
 
-namespace views {
-class Label;
-}
-
 namespace autofill {
 
 class AutofillPopupController;
 
-// Child view representing one row (i.e., one suggestion) in the Autofill
-// Popup.
+// Child view representing one row in the Autofill Popup. This could represent
+// a UI control (e.g., a suggestion which can be autofilled), or decoration like
+// separators.
 class AutofillPopupRowView : public views::View {
  public:
-  AutofillPopupRowView(AutofillPopupController* controller, int line_number);
-
-  ~AutofillPopupRowView() override {}
-
-  void AcceptSelection();
+  ~AutofillPopupRowView() override = default;
   void SetSelected(bool is_selected);
-  void RefreshStyle();
 
   // views::View:
-  // TODO(tmartino): Consolidate and deprecate code in AutofillPopupBaseView
-  // where overlap exists with these events.
-  void OnMouseEntered(const ui::MouseEvent& event) override;
-  void OnMouseReleased(const ui::MouseEvent& event) override;
+  // Drags and presses on any row should be a no-op; subclasses instead rely on
+  // entry/release events. Returns true to indicate that those events have been
+  // processed (i.e., intentionally ignored).
   bool OnMouseDragged(const ui::MouseEvent& event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
-  void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
 
- private:
-  // views::View:
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  void CreateContent();
+ protected:
+  AutofillPopupRowView(AutofillPopupController* controller, int line_number);
+
+  // Init handles initialization tasks which require virtual methods. Subclasses
+  // should have private/protected constructors and implement a static Create
+  // method which calls Init before returning.
+  void Init();
+
+  virtual void CreateContent() = 0;
+  virtual void RefreshStyle() = 0;
+  virtual std::unique_ptr<views::Background> CreateBackground() = 0;
 
   AutofillPopupController* controller_;
   const int line_number_;
-  bool is_separator_ = false;  // overwritten in ctor
-  bool is_warning_ = false;    // overwritten in ctor
+  bool is_warning_ = false;  // overwritten in ctor
   bool is_selected_ = false;
-
-  views::Label* text_label_ = nullptr;
-  views::Label* subtext_label_ = nullptr;
-
-  SkColor text_color_ = gfx::kPlaceholderColor;
-  SkColor text_selected_color_ = gfx::kPlaceholderColor;
-  SkColor subtext_color_ = gfx::kPlaceholderColor;
-  SkColor subtext_selected_color_ = gfx::kPlaceholderColor;
-
-  DISALLOW_COPY_AND_ASSIGN(AutofillPopupRowView);
 };
 
 // Views implementation for the autofill and password suggestion.
@@ -91,9 +78,9 @@
   gfx::Size CalculatePreferredSize() const override;
 
   // AutofillPopupBaseView:
-  // TODO(tmartino): Remove these overrides and the corresponding methods in
-  // AutofillPopupBaseView once deprecation of AutofillPopupViewViews is
-  // complete.
+  // TODO(crbug.com/831603): Remove these overrides and the corresponding
+  // methods in AutofillPopupBaseView once deprecation of
+  // AutofillPopupViewViews is complete.
   void OnMouseExited(const ui::MouseEvent& event) override {}
   void OnMouseMoved(const ui::MouseEvent& event) override {}
 
diff --git a/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc b/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc
index 6ae5fab..c7b7f89b 100644
--- a/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc
+++ b/chrome/browser/ui/views/extensions/extension_dialog_interactive_uitest.cc
@@ -15,7 +15,7 @@
 
 namespace {
 
-class ExtensionDialogUiTest : public ExtensionBrowserTest {
+class ExtensionDialogUiTest : public extensions::ExtensionBrowserTest {
  public:
   ExtensionDialogUiTest() = default;
   ~ExtensionDialogUiTest() override = default;
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 157755c..4b33dde9 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -45,7 +45,8 @@
 using extensions::PermissionMessage;
 using extensions::PermissionMessages;
 
-class ExtensionInstallDialogViewTestBase : public ExtensionBrowserTest {
+class ExtensionInstallDialogViewTestBase
+    : public extensions::ExtensionBrowserTest {
  protected:
   ExtensionInstallDialogViewTestBase();
 
@@ -68,9 +69,9 @@
     : extension_(nullptr), web_contents_(nullptr) {}
 
 void ExtensionInstallDialogViewTestBase::SetUpOnMainThread() {
-  ExtensionBrowserTest::SetUpOnMainThread();
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
 
-  extension_ = ExtensionBrowserTest::LoadExtension(test_data_dir_.AppendASCII(
+  extension_ = LoadExtension(test_data_dir_.AppendASCII(
       "install_prompt/permissions_scrollbar_regression"));
 
   web_contents_ = browser()->tab_strip_model()->GetWebContentsAt(0);
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
index 8d7793f..1f74adf9 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
@@ -308,10 +308,8 @@
   void ShowUi(const std::string& name) override {
     extensions::ExtensionBuilder extension_builder("ExtensionForRemoval");
     if (extension_origin_ == EXTENSION_FROM_WEBSTORE) {
-      extensions::DictionaryBuilder update_url;
-      update_url.Set("update_url",
-                     extension_urls::GetWebstoreUpdateUrl().spec());
-      extension_builder.MergeManifest(update_url.Build());
+      extension_builder.SetManifestKey(
+          "update_url", extension_urls::GetWebstoreUpdateUrl().spec());
     }
 
     extension_ = extension_builder.Build();
diff --git a/chrome/browser/ui/views/frame/DEPS b/chrome/browser/ui/views/frame/DEPS
new file mode 100644
index 0000000..1cad5de
--- /dev/null
+++ b/chrome/browser/ui/views/frame/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+  # This class is only used in classic Ash.
+  "browser_non_client_frame_view_ash\.*": [
+    "+ash",
+  ],
+}
diff --git a/chrome/browser/ui/views/frame/browser_frame_mus.cc b/chrome/browser/ui/views/frame/browser_frame_mus.cc
index af8d7e4..936b0a7f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_mus.cc
@@ -52,7 +52,6 @@
   params.delegate = browser_view_;
   std::map<std::string, std::vector<uint8_t>> properties =
       views::MusClient::ConfigurePropertiesFromParams(params);
-  const std::string chrome_app_id(extension_misc::kChromeAppId);
   // Indicates mash shouldn't handle immersive, rather we will.
   properties[ui::mojom::WindowManager::kDisableImmersive_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(true);
@@ -66,6 +65,9 @@
   properties[ui::mojom::WindowManager::kShelfItemType_Property] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           static_cast<int64_t>(ash::TYPE_BROWSER_SHORTCUT));
+  properties[ui::mojom::WindowManager::kWindowTitleShown_Property] =
+      mojo::ConvertTo<std::vector<uint8_t>>(
+          static_cast<int64_t>(browser_view_->ShouldShowWindowTitle()));
 
   // TODO(estade): to match classic Ash, this property should be toggled to true
   // for non-popups after the window is initially shown.
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 b5a40a3..f9629a6 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
@@ -39,7 +39,6 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/frame/browser_frame.h"
 #include "chrome/browser/ui/views/frame/browser_frame_ash.h"
-#include "chrome/browser/ui/views/frame/browser_frame_header_ash.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
@@ -439,7 +438,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserFrameHeaderAsh::AppearanceProvider:
+// ash::CustomFrameHeader::AppearanceProvider:
 
 SkColor BrowserNonClientFrameViewAsh::GetFrameHeaderColor(bool active) {
   return GetFrameColor(active);
@@ -640,71 +639,72 @@
 
 std::unique_ptr<ash::FrameHeader>
 BrowserNonClientFrameViewAsh::CreateFrameHeader() {
+  std::unique_ptr<ash::FrameHeader> header;
+
   Browser* browser = browser_view()->browser();
   if (!UsePackagedAppHeaderStyle()) {
-    auto browser_frame_header = std::make_unique<BrowserFrameHeaderAsh>();
-    browser_frame_header->Init(
-        this, this, !browser_view()->IsRegularOrGuestSession(), window_icon_,
-        caption_button_container_, back_button_);
-    return browser_frame_header;
-  }
-  std::unique_ptr<ash::DefaultFrameHeader> default_frame_header =
-      std::make_unique<ash::DefaultFrameHeader>(frame(), this,
-                                                caption_button_container_);
+    auto browser_frame_header = std::make_unique<ash::CustomFrameHeader>();
+    browser_frame_header->Init(this, this,
+                               !browser_view()->IsRegularOrGuestSession(),
+                               caption_button_container_);
+    header = std::move(browser_frame_header);
+  } else {
+    std::unique_ptr<ash::DefaultFrameHeader> default_frame_header =
+        std::make_unique<ash::DefaultFrameHeader>(frame(), this,
+                                                  caption_button_container_);
+    // TODO(alancutter): Move this branch into a new HostedAppFrameHeader class.
+    if (extensions::HostedAppBrowserController::
+            IsForExperimentalHostedAppBrowser(browser)) {
+      SkColor active_color = ash::FrameCaptionButton::GetButtonColor(
+          ash::FrameCaptionButton::ColorMode::kDefault,
+          ash::DefaultFrameHeader::GetDefaultFrameColor());
 
-  // TODO(alancutter): Move this branch into a new HostedAppFrameHeader class.
-  if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
-          browser)) {
-    // Hosted apps apply a theme color if specified by the extension.
-    base::Optional<SkColor> theme_color =
-        browser->hosted_app_controller()->GetThemeColor();
-    if (theme_color) {
-      SkColor opaque_theme_color =
-          SkColorSetA(theme_color.value(), SK_AlphaOPAQUE);
-      default_frame_header->SetThemeColor(opaque_theme_color);
+      // Hosted apps apply a theme color if specified by the extension.
+      base::Optional<SkColor> theme_color =
+          browser->hosted_app_controller()->GetThemeColor();
+      if (theme_color) {
+        theme_color = SkColorSetA(theme_color.value(), SK_AlphaOPAQUE);
+        default_frame_header->SetThemeColor(*theme_color);
+        active_color = ash::FrameCaptionButton::GetButtonColor(
+            ash::FrameCaptionButton::ColorMode::kThemed, *theme_color);
+      }
+
+      // Add the container for extra hosted app buttons (e.g app menu button).
+      const float inactive_alpha_ratio =
+          ash::FrameCaptionButton::GetInactiveButtonColorAlphaRatio();
+      SkColor inactive_color =
+          SkColorSetA(active_color, 255 * inactive_alpha_ratio);
+      hosted_app_button_container_ = new HostedAppButtonContainer(
+          browser_view(), active_color, inactive_color);
+      caption_button_container_->AddChildViewAt(hosted_app_button_container_,
+                                                0);
+
+      // Add the origin text.
+      frame_header_origin_text_ =
+          std::make_unique<ash::FrameHeaderOriginText>(
+              browser->hosted_app_controller()->GetFormattedUrlOrigin(),
+              active_color, inactive_color)
+              .release();
+      AddChildView(frame_header_origin_text_);
+
+      // Schedule the title bar animation.
+      constexpr base::TimeDelta kTitlebarAnimationDelay =
+          base::TimeDelta::FromMilliseconds(750);
+      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(&BrowserNonClientFrameViewAsh::StartHostedAppAnimation,
+                         weak_factory_.GetWeakPtr()),
+          kTitlebarAnimationDelay);
+    } else if (!browser->is_app()) {
+      default_frame_header->SetFrameColors(BrowserFrameAsh::kMdWebUiFrameColor,
+                                           BrowserFrameAsh::kMdWebUiFrameColor);
     }
-
-    // Add the container for extra hosted app buttons (e.g app menu button).
-    SkColor active_color = ash::FrameCaptionButton::GetButtonColor(
-        theme_color ? ash::FrameCaptionButton::ColorMode::kThemed
-                    : ash::FrameCaptionButton::ColorMode::kDefault,
-        default_frame_header->GetActiveFrameColor());
-    const float inactive_alpha_ratio =
-        ash::FrameCaptionButton::GetInactiveButtonColorAlphaRatio();
-    SkColor inactive_color =
-        SkColorSetA(active_color, 255 * inactive_alpha_ratio);
-    hosted_app_button_container_ = new HostedAppButtonContainer(
-        browser_view(), active_color, inactive_color);
-    caption_button_container_->AddChildViewAt(hosted_app_button_container_, 0);
-
-    // Add the origin text.
-    frame_header_origin_text_ =
-        std::make_unique<ash::FrameHeaderOriginText>(
-            browser->hosted_app_controller()->GetFormattedUrlOrigin(),
-            active_color, inactive_color)
-            .release();
-    AddChildView(frame_header_origin_text_);
-
-    // Schedule the title bar animation.
-    constexpr base::TimeDelta kTitlebarAnimationDelay =
-        base::TimeDelta::FromMilliseconds(750);
-    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&BrowserNonClientFrameViewAsh::StartHostedAppAnimation,
-                       weak_factory_.GetWeakPtr()),
-        kTitlebarAnimationDelay);
-  } else if (!browser->is_app()) {
-    default_frame_header->SetFrameColors(BrowserFrameAsh::kMdWebUiFrameColor,
-                                         BrowserFrameAsh::kMdWebUiFrameColor);
+    header = std::move(default_frame_header);
   }
 
-  if (back_button_)
-    default_frame_header->set_back_button(back_button_);
-
-  if (window_icon_)
-    default_frame_header->set_left_header_view(window_icon_);
-
-  return default_frame_header;
+  header->SetBackButton(back_button_);
+  header->SetLeftHeaderView(window_icon_);
+  return header;
 }
 
 void BrowserNonClientFrameViewAsh::StartHostedAppAnimation() {
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 03298041..80dfc1f9 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,13 +7,13 @@
 
 #include <memory>
 
+#include "ash/frame/custom_frame_header.h"
 #include "ash/public/interfaces/split_view.mojom.h"
 #include "ash/shell_observer.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "chrome/browser/command_observer.h"
 #include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
-#include "chrome/browser/ui/views/frame/browser_frame_header_ash.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"
@@ -35,7 +35,7 @@
 // Provides the BrowserNonClientFrameView for Chrome OS.
 class BrowserNonClientFrameViewAsh
     : public BrowserNonClientFrameView,
-      public BrowserFrameHeaderAsh::AppearanceProvider,
+      public ash::CustomFrameHeader::AppearanceProvider,
       public ash::ShellObserver,
       public TabletModeClientObserver,
       public TabIconViewModel,
@@ -79,7 +79,7 @@
   gfx::Size GetMinimumSize() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
 
-  // BrowserFrameHeaderAsh::AppearanceProvider:
+  // ash::CustomFrameHeader::AppearanceProvider:
   SkColor GetFrameHeaderColor(bool active) override;
   gfx::ImageSkia GetFrameHeaderImage(bool active) override;
   gfx::ImageSkia GetFrameHeaderOverlayImage(bool active) override;
@@ -138,7 +138,6 @@
                            FrameLayoutToggleTabletMode);
 
   friend class HostedAppNonClientFrameViewAshTest;
-  friend class BrowserFrameHeaderAsh;
   friend class ImmersiveModeControllerAshHostedAppBrowserTest;
 
   // Distance between the right edge of the NonClientFrameView and the tab
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 4fa8ce4..12ba5cb 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
@@ -74,6 +74,7 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/vector_icon_types.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/window_util.h"
 
@@ -426,25 +427,30 @@
   ash::FrameCaptionButtonContainerView::TestApi test(
       frame_view->caption_button_container_);
   widget->Maximize();
-  // Restore icon for size button in maximized window state.
-  EXPECT_EQ(&ash::kWindowControlRestoreIcon,
-            test.size_button()->icon_definition_for_test());
+
+  // Restore icon for size button in maximized window state. Compare by name
+  // because the address may not be the same for different build targets in the
+  // component build.
+  EXPECT_EQ(*ash::kWindowControlRestoreIcon.name,
+            *test.size_button()->icon_definition_for_test()->name);
   widget->Minimize();
+
   // When entering tablet mode in minimized window state, size button should not
   // get updated.
   TabletModeClient::Get()->OnTabletModeToggled(true);
-  EXPECT_EQ(&ash::kWindowControlRestoreIcon,
-            test.size_button()->icon_definition_for_test());
+  EXPECT_EQ(*ash::kWindowControlRestoreIcon.name,
+            *test.size_button()->icon_definition_for_test()->name);
   // When leaving tablet mode in minimized window state, size button should not
   // get updated.
   TabletModeClient::Get()->OnTabletModeToggled(false);
-  EXPECT_EQ(&ash::kWindowControlRestoreIcon,
-            test.size_button()->icon_definition_for_test());
+  EXPECT_EQ(*ash::kWindowControlRestoreIcon.name,
+            *test.size_button()->icon_definition_for_test()->name);
+
   // When unminimizing in non-tablet mode, size button should match with
   // maximized window state, which is restore icon.
   ::wm::Unminimize(widget->GetNativeWindow());
-  EXPECT_EQ(&ash::kWindowControlRestoreIcon,
-            test.size_button()->icon_definition_for_test());
+  EXPECT_EQ(*ash::kWindowControlRestoreIcon.name,
+            *test.size_button()->icon_definition_for_test()->name);
 }
 
 // This is a regression test that session restore minimized browser should
@@ -694,8 +700,8 @@
 
 // Tests that a web app's theme color is set.
 IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest, ThemeColor) {
-  EXPECT_EQ(GetThemeColor(), frame_header_->GetActiveFrameColor());
-  EXPECT_EQ(GetThemeColor(), frame_header_->GetInactiveFrameColor());
+  EXPECT_EQ(GetThemeColor(), frame_header_->active_frame_color_for_testing());
+  EXPECT_EQ(GetThemeColor(), frame_header_->inactive_frame_color_for_testing());
   EXPECT_EQ(SK_ColorWHITE, GetActiveIconColor(hosted_app_button_container_));
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
index 3cb3555..3d39234 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "ui/base/theme_provider.h"
 
-using BrowserNonClientFrameViewBrowserTest = ExtensionBrowserTest;
+using BrowserNonClientFrameViewBrowserTest = extensions::ExtensionBrowserTest;
 
 // Test is Flaky on Windows see crbug.com/600201.
 #if defined(OS_WIN)
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
index 213941d..9432f25 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/frame/browser_frame.h"
-#include "chrome/browser/ui/views/frame/browser_frame_header_ash.h"
 #include "chrome/browser/ui/views/frame/browser_frame_mus.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
@@ -39,6 +38,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/ash_layout_constants.h"
+#include "ash/public/cpp/window_properties.h"
 #endif
 
 #if !defined(OS_CHROMEOS)
@@ -102,6 +102,8 @@
     AddChildView(window_icon_);
     window_icon_->Update();
   }
+
+  OnThemeChanged();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -350,6 +352,18 @@
   return gfx::Size(min_width, min_client_view_size.height());
 }
 
+void BrowserNonClientFrameViewMus::OnThemeChanged() {
+#if defined(OS_CHROMEOS)
+  gfx::ImageSkia active_frame_image = GetFrameImage(true);
+  if (active_frame_image.isNull()) {
+    frame()->GetNativeWindow()->ClearProperty(ash::kFrameImageActiveKey);
+  } else {
+    frame()->GetNativeWindow()->SetProperty(
+        ash::kFrameImageActiveKey, new gfx::ImageSkia(active_frame_image));
+  }
+#endif
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // TabIconViewModel:
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
index 376cfa2a..bac1874 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
@@ -57,6 +57,7 @@
   const char* GetClassName() const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   gfx::Size GetMinimumSize() const override;
+  void OnThemeChanged() override;
 
   // TabIconViewModel:
   bool ShouldTabIconViewAnimate() const override;
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
index 949d029..4e16271 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -41,7 +41,7 @@
 #include "extensions/common/extension.h"
 #include "ui/views/win/hwnd_util.h"
 
-typedef ExtensionBrowserTest BrowserWindowPropertyManagerTest;
+typedef extensions::ExtensionBrowserTest BrowserWindowPropertyManagerTest;
 
 namespace {
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
index e83285db..3e6a1d6 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
@@ -36,7 +36,7 @@
 #include "ui/views/animation/test/ink_drop_host_view_test_api.h"
 
 class ImmersiveModeControllerAshHostedAppBrowserTest
-    : public ExtensionBrowserTest {
+    : public extensions::ExtensionBrowserTest {
  public:
   ImmersiveModeControllerAshHostedAppBrowserTest()
       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
@@ -81,17 +81,17 @@
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
     ProfileIOData::SetCertVerifierForTesting(&mock_cert_verifier_);
   }
 
   void TearDownInProcessBrowserTestFixture() override {
     ProfileIOData::SetCertVerifierForTesting(nullptr);
-    ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
+    extensions::ExtensionBrowserTest::TearDownInProcessBrowserTestFixture();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ExtensionBrowserTest::SetUpCommandLine(command_line);
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kUseMockCertVerifierForTesting);
   }
 
diff --git a/chrome/browser/ui/views/harmony/layout_provider_unittest.cc b/chrome/browser/ui/views/harmony/layout_provider_unittest.cc
index e5ab434b..5bdc2b5 100644
--- a/chrome/browser/ui/views/harmony/layout_provider_unittest.cc
+++ b/chrome/browser/ui/views/harmony/layout_provider_unittest.cc
@@ -70,11 +70,7 @@
 // TODO(tapted): Smoke them out and figure out why.
 
 #if defined(OS_MACOSX)
-  if (base::mac::IsOS10_9()) {
-    EXPECT_EQ(6, label_font.GetExpectedTextWidth(1));
-  } else {
-    EXPECT_EQ(10, label_font.GetExpectedTextWidth(1));
-  }
+  EXPECT_EQ(10, label_font.GetExpectedTextWidth(1));
 #else
   EXPECT_EQ(6, label_font.GetExpectedTextWidth(1));
 // Some Windows bots may say 5.
diff --git a/chrome/browser/ui/views/ime/ime_warning_bubble_browsertest.cc b/chrome/browser/ui/views/ime/ime_warning_bubble_browsertest.cc
index 1c20dc4..ad9b6e6 100644
--- a/chrome/browser/ui/views/ime/ime_warning_bubble_browsertest.cc
+++ b/chrome/browser/ui/views/ime/ime_warning_bubble_browsertest.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ui/views/ime/ime_warning_bubble_view.h"
 #include "ui/views/controls/button/checkbox.h"
 
-class ImeWarningBubbleTest : public ExtensionBrowserTest {
+class ImeWarningBubbleTest : public extensions::ExtensionBrowserTest {
  public:
   ImeWarningBubbleTest();
   ~ImeWarningBubbleTest() override {}
@@ -40,9 +40,8 @@
 
 void ImeWarningBubbleTest::SetUpOnMainThread() {
   ToolbarActionsBar::disable_animations_for_testing_ = true;
-  ExtensionBrowserTest::SetUpOnMainThread();
-  extension_ = ExtensionBrowserTest::LoadExtension(
-      test_data_dir_.AppendASCII("input_ime"));
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
+  extension_ = LoadExtension(test_data_dir_.AppendASCII("input_ime"));
   callback_ =
       base::Bind(&ImeWarningBubbleTest::OnPermissionBubbleFinished,
                  base::Unretained(this));
diff --git a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
index af140ea..4630f97 100644
--- a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
@@ -175,6 +175,10 @@
   UpdateIcon();
 }
 
+void BubbleIconView::OnThemeChanged() {
+  UpdateIcon();
+}
+
 void BubbleIconView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
   image_->SetPaintToLayer();
   image_->layer()->SetFillsBoundsOpaquely(false);
@@ -254,10 +258,11 @@
 void BubbleIconView::UpdateIcon() {
   const ui::NativeTheme* theme = GetNativeTheme();
   SkColor icon_color =
-      active_
-          ? theme->GetSystemColor(
-              ui::NativeTheme::kColorId_ProminentButtonColor)
-          : GetInkDropBaseColor();
+      active_ ? theme->GetSystemColor(
+                    ui::NativeTheme::kColorId_ProminentButtonColor)
+              : GetOmniboxColor(OmniboxPart::LOCATION_BAR_SECURITY_CHIP,
+                                delegate_->GetTint(),
+                                OmniboxPartState::CHIP_DEFAULT);
   image_->SetImage(gfx::CreateVectorIcon(
       GetVectorIcon(), GetLayoutConstant(LOCATION_BAR_ICON_SIZE), icon_color));
 }
diff --git a/chrome/browser/ui/views/location_bar/bubble_icon_view.h b/chrome/browser/ui/views/location_bar/bubble_icon_view.h
index 6eae406..e95e2bc 100644
--- a/chrome/browser/ui/views/location_bar/bubble_icon_view.h
+++ b/chrome/browser/ui/views/location_bar/bubble_icon_view.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/animation/ink_drop_host_view.h"
 #include "ui/views/controls/image_view.h"
@@ -35,6 +36,7 @@
   class Delegate {
    public:
     virtual content::WebContents* GetWebContentsForBubbleIconView() = 0;
+    virtual OmniboxTint GetTint() = 0;
   };
 
   void Init();
@@ -59,7 +61,7 @@
 
   BubbleIconView(CommandUpdater* command_updater,
                  int command_id,
-                 Delegate* delegate = nullptr);
+                 Delegate* delegate);
   ~BubbleIconView() override;
 
   // Returns true if a related bubble is showing.
@@ -98,6 +100,7 @@
   void ViewHierarchyChanged(
       const ViewHierarchyChangedDetails& details) override;
   void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
+  void OnThemeChanged() override;
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
   std::unique_ptr<views::InkDrop> CreateInkDrop() override;
diff --git a/chrome/browser/ui/views/location_bar/find_bar_icon.cc b/chrome/browser/ui/views/location_bar/find_bar_icon.cc
index 0a2b7d1..e46fb4d 100644
--- a/chrome/browser/ui/views/location_bar/find_bar_icon.cc
+++ b/chrome/browser/ui/views/location_bar/find_bar_icon.cc
@@ -8,7 +8,8 @@
 #include "components/toolbar/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 
-FindBarIcon::FindBarIcon() : BubbleIconView(nullptr, 0) {}
+FindBarIcon::FindBarIcon(BubbleIconView::Delegate* delegate)
+    : BubbleIconView(nullptr, 0, delegate) {}
 
 FindBarIcon::~FindBarIcon() {}
 
diff --git a/chrome/browser/ui/views/location_bar/find_bar_icon.h b/chrome/browser/ui/views/location_bar/find_bar_icon.h
index adf03b2b..7af0dc9f 100644
--- a/chrome/browser/ui/views/location_bar/find_bar_icon.h
+++ b/chrome/browser/ui/views/location_bar/find_bar_icon.h
@@ -11,7 +11,7 @@
 // The find icon to show when the find bar is visible.
 class FindBarIcon : public BubbleIconView {
  public:
-  FindBarIcon();
+  explicit FindBarIcon(BubbleIconView::Delegate* delegate);
   ~FindBarIcon() override;
 
   void SetActive(bool activate, bool should_animate);
diff --git a/chrome/browser/ui/views/location_bar/intent_picker_view.cc b/chrome/browser/ui/views/location_bar/intent_picker_view.cc
index 320846e..c476d57 100644
--- a/chrome/browser/ui/views/location_bar/intent_picker_view.cc
+++ b/chrome/browser/ui/views/location_bar/intent_picker_view.cc
@@ -19,8 +19,9 @@
 class WebContents;
 }
 
-IntentPickerView::IntentPickerView(Browser* browser)
-    : BubbleIconView(nullptr, 0), browser_(browser) {
+IntentPickerView::IntentPickerView(Browser* browser,
+                                   BubbleIconView::Delegate* delegate)
+    : BubbleIconView(nullptr, 0, delegate), browser_(browser) {
   if (browser_) {
     intent_picker_controller_ =
         std::make_unique<arc::IntentPickerController>(browser_);
diff --git a/chrome/browser/ui/views/location_bar/intent_picker_view.h b/chrome/browser/ui/views/location_bar/intent_picker_view.h
index e9f98ace..e01549c8 100644
--- a/chrome/browser/ui/views/location_bar/intent_picker_view.h
+++ b/chrome/browser/ui/views/location_bar/intent_picker_view.h
@@ -19,7 +19,7 @@
 // The entry point for the intent picker.
 class IntentPickerView : public BubbleIconView {
  public:
-  explicit IntentPickerView(Browser* browser);
+  IntentPickerView(Browser* browser, BubbleIconView::Delegate* delegate);
   ~IntentPickerView() override;
 
   // BubbleIconView:
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index aaae7a4..0e6a3e6d 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -123,23 +123,6 @@
   return ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
 }
 
-OmniboxTint GetTintForProfile(Profile* profile) {
-  ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile);
-  if (theme_service->UsingDefaultTheme()) {
-    return profile->GetProfileType() == Profile::INCOGNITO_PROFILE
-               ? OmniboxTint::DARK
-               : OmniboxTint::LIGHT;
-  }
-
-  // Check for GTK on Desktop Linux.
-  if (theme_service->IsSystemThemeDistinctFromDefaultTheme() &&
-      theme_service->UsingSystemTheme())
-    return OmniboxTint::NATIVE;
-
-  // TODO(tapted): Infer a tint from theme colors?
-  return OmniboxTint::LIGHT;
-}
-
 // Returns true when a views::FocusRing should be used.
 bool ShouldUseFocusRingView(bool show_focus_ring) {
   return (show_focus_ring && LocationBarView::IsRounded()) ||
@@ -187,7 +170,7 @@
       browser_(browser),
       delegate_(delegate),
       is_popup_mode_(is_popup_mode),
-      tint_(GetTintForProfile(profile)) {
+      tint_(GetTint()) {
   edit_bookmarks_enabled_.Init(
       bookmarks::prefs::kEditBookmarksEnabled, profile->GetPrefs(),
       base::Bind(&LocationBarView::UpdateWithoutTabRestore,
@@ -260,7 +243,7 @@
     AddChildView(image_view);
   }
 
-  zoom_view_ = new ZoomView(delegate_);
+  zoom_view_ = new ZoomView(delegate_, this);
   bubble_icons_.push_back(zoom_view_);
   manage_passwords_icon_view_ =
       new ManagePasswordsIconViews(command_updater(), this);
@@ -277,12 +260,13 @@
 #if defined(OS_CHROMEOS)
   if (browser_)
     bubble_icons_.push_back(intent_picker_view_ =
-                                new IntentPickerView(browser_));
+                                new IntentPickerView(browser_, this));
 #endif
-  bubble_icons_.push_back(find_bar_icon_ = new FindBarIcon());
-  if (browser_)
-    bubble_icons_.push_back(star_view_ =
-                                new StarView(command_updater(), browser_));
+  bubble_icons_.push_back(find_bar_icon_ = new FindBarIcon(this));
+  if (browser_) {
+    bubble_icons_.push_back(
+        star_view_ = new StarView(command_updater(), browser_, this));
+  }
 
   std::for_each(bubble_icons_.begin(), bubble_icons_.end(),
                 [this](BubbleIconView* icon_view) -> void {
@@ -643,7 +627,7 @@
 }
 
 void LocationBarView::OnThemeChanged() {
-  tint_ = GetTintForProfile(profile());
+  tint_ = GetTint();
 }
 
 void LocationBarView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
@@ -739,6 +723,23 @@
   return GetWebContents();
 }
 
+OmniboxTint LocationBarView::GetTint() {
+  ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile());
+  if (theme_service->UsingDefaultTheme()) {
+    return profile()->GetProfileType() == Profile::INCOGNITO_PROFILE
+               ? OmniboxTint::DARK
+               : OmniboxTint::LIGHT;
+  }
+
+  // Check for GTK on Desktop Linux.
+  if (theme_service->IsSystemThemeDistinctFromDefaultTheme() &&
+      theme_service->UsingSystemTheme())
+    return OmniboxTint::NATIVE;
+
+  // TODO(tapted): Infer a tint from theme colors?
+  return OmniboxTint::LIGHT;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // LocationBarView, public static methods:
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 77d8183..6154eb7 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -242,9 +242,6 @@
   ContentSettingBubbleModelDelegate* GetContentSettingBubbleModelDelegate()
       override;
 
-  // BubbleIconView::Delegate:
-  content::WebContents* GetWebContentsForBubbleIconView() override;
-
   // ZoomEventManagerObserver:
   // Updates the view for the zoom icon when default zoom levels change.
   void OnDefaultZoomLevelChanged() override;
@@ -374,6 +371,10 @@
                            const gfx::Point& press_pt,
                            const gfx::Point& p) override;
 
+  // BubbleIconView::Delegate:
+  content::WebContents* GetWebContentsForBubbleIconView() override;
+  OmniboxTint GetTint() override;
+
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationEnded(const gfx::Animation* animation) override;
diff --git a/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
index ed01ce2..6331dc4 100644
--- a/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/selected_keyword_view_interactive_uitest.cc
@@ -22,7 +22,7 @@
   }
 }
 
-class SelectedKeywordViewTest : public ExtensionBrowserTest {
+class SelectedKeywordViewTest : public extensions::ExtensionBrowserTest {
  public:
   SelectedKeywordViewTest() = default;
   ~SelectedKeywordViewTest() override = default;
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index ed11dcb..48fca38 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -20,8 +20,10 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/widget/widget_observer.h"
 
-StarView::StarView(CommandUpdater* command_updater, Browser* browser)
-    : BubbleIconView(command_updater, IDC_BOOKMARK_PAGE),
+StarView::StarView(CommandUpdater* command_updater,
+                   Browser* browser,
+                   BubbleIconView::Delegate* delegate)
+    : BubbleIconView(command_updater, IDC_BOOKMARK_PAGE, delegate),
       browser_(browser),
       bookmark_promo_observer_(this) {
   set_id(VIEW_ID_STAR_BUTTON);
diff --git a/chrome/browser/ui/views/location_bar/star_view.h b/chrome/browser/ui/views/location_bar/star_view.h
index a6f6c00..1d4d86fe 100644
--- a/chrome/browser/ui/views/location_bar/star_view.h
+++ b/chrome/browser/ui/views/location_bar/star_view.h
@@ -15,7 +15,9 @@
 // The star icon to show a bookmark bubble.
 class StarView : public BubbleIconView, public views::WidgetObserver {
  public:
-  StarView(CommandUpdater* command_updater, Browser* browser);
+  StarView(CommandUpdater* command_updater,
+           Browser* browser,
+           BubbleIconView::Delegate* delegate);
   ~StarView() override;
 
   // Toggles the star on or off.
diff --git a/chrome/browser/ui/views/location_bar/zoom_view.cc b/chrome/browser/ui/views/location_bar/zoom_view.cc
index e2b468de..0b1fc03 100644
--- a/chrome/browser/ui/views/location_bar/zoom_view.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_view.cc
@@ -16,8 +16,9 @@
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/size.h"
 
-ZoomView::ZoomView(LocationBarView::Delegate* location_bar_delegate)
-    : BubbleIconView(nullptr, 0),
+ZoomView::ZoomView(LocationBarView::Delegate* location_bar_delegate,
+                   BubbleIconView::Delegate* delegate)
+    : BubbleIconView(nullptr, 0, delegate),
       location_bar_delegate_(location_bar_delegate),
       icon_(&kZoomMinusIcon) {
   Update(nullptr);
diff --git a/chrome/browser/ui/views/location_bar/zoom_view.h b/chrome/browser/ui/views/location_bar/zoom_view.h
index cc2496dd..20694ca 100644
--- a/chrome/browser/ui/views/location_bar/zoom_view.h
+++ b/chrome/browser/ui/views/location_bar/zoom_view.h
@@ -20,7 +20,8 @@
   // WebContents. Because the current WebContents changes as the user switches
   // tabs, a LocationBarView::Delegate is supplied to queried for the current
   // WebContents when needed.
-  explicit ZoomView(LocationBarView::Delegate* location_bar_delegate);
+  ZoomView(LocationBarView::Delegate* location_bar_delegate,
+           BubbleIconView::Delegate* delegate);
   ~ZoomView() override;
 
   // Updates the image and its tooltip appropriately, hiding or showing the icon
diff --git a/chrome/browser/ui/views/menu_controller_interactive_uitest.cc b/chrome/browser/ui/views/menu_controller_interactive_uitest.cc
index 276eb16..2c3da9d 100644
--- a/chrome/browser/ui/views/menu_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/menu_controller_interactive_uitest.cc
@@ -4,6 +4,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/views/menu_test_base.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/submenu_view.h"
@@ -55,7 +56,14 @@
 typedef MenuControllerMnemonicTest<ui::VKEY_DIVIDE,1>
     MenuControllerMnemonicTestMnemonicMatch;
 
-VIEW_TEST(MenuControllerMnemonicTestMnemonicMatch, MnemonicMatch);
+#if defined(OS_MACOSX)
+// Mnemonics aren't used on macOS.
+#define MAYBE_MnemonicMatch DISABLED_MnemonicMatch
+#else
+#define MAYBE_MnemonicMatch MnemonicMatch
+#endif
+
+VIEW_TEST(MenuControllerMnemonicTestMnemonicMatch, MAYBE_MnemonicMatch);
 
 // Pressing a key which matches the first letter of the menu item's title
 // should execute the command for that menu item.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index 69aa0ab..3ae09a4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -24,18 +24,22 @@
 
 // The minimum vertical margin that should be used above and below each
 // suggestion.
-static const int kMinVerticalMargin = 1;
+static constexpr int kMinVerticalMargin = 1;
 
 // The vertical padding to provide each RenderText in addition to the height of
 // the font. Where possible, RenderText uses this additional space to vertically
 // center the cap height of the font instead of centering the entire font.
-static const int kVerticalPadding = 4;
+static constexpr int kVerticalPadding = 4;
 
 // TODO(dschuyler): Perhaps this should be based on the font size
 // instead of hardcoded to 2 dp (e.g. by adding a space in an
 // appropriate font to the beginning of the description, then reducing
 // the additional padding here to zero).
-static const int kAnswerIconToTextPadding = 2;
+static constexpr int kAnswerIconToTextPadding = 2;
+
+// The edge length of the rich suggestions images.
+static constexpr int kRichImageSize = 32;
+static constexpr int kRichImageCornerRadius = 4;
 
 // Returns the horizontal offset that ensures icons align vertically with the
 // Omnibox icon.
@@ -127,7 +131,9 @@
   flags.setAntiAlias(true);
   flags.setStyle(cc::PaintFlags::kStrokeAndFill_Style);
   flags.setColor(color_);
-  canvas->sk_canvas()->drawOval(gfx::RectToSkRect(gfx::Rect(size_)), flags);
+  canvas->sk_canvas()->drawRoundRect(gfx::RectToSkRect(gfx::Rect(size_)),
+                                     kRichImageCornerRadius,
+                                     kRichImageCornerRadius, flags);
 }
 
 }  // namespace
@@ -208,10 +214,9 @@
     extensions::image_util::ParseHexColorString(match.image_dominant_color,
                                                 &color);
     color = SkColorSetA(color, 0x40);  // 25% transparency (arbitrary).
-    int image_edge_length = description_view_->GetLineHeight();
     image_view_->SetImage(
         gfx::CanvasImageSource::MakeImageSkia<PlaceholderImageSource>(
-            gfx::Size(image_edge_length, image_edge_length), color));
+            gfx::Size(kRichImageSize, kRichImageSize), color));
   } else {
     // Single-line layout doesn't use the image.
     image_view_->SetSize(gfx::Size());
@@ -267,10 +272,10 @@
 void OmniboxMatchCellView::LayoutRichSuggestion() {
   int x = GetIconAlignmentOffset() + HorizontalPadding();
   int y = GetVerticalInsets(text_height_, /*is_old_style_answer=*/false).top();
-  int image_edge_length = text_height_ * 2;
-  image_view_->SetImageSize(gfx::Size(image_edge_length, image_edge_length));
-  image_view_->SetBounds(x, y, image_edge_length, image_edge_length);
-  x += image_edge_length + HorizontalPadding();
+  image_view_->SetImageSize(gfx::Size(kRichImageSize, kRichImageSize));
+  image_view_->SetBounds(x, y + (text_height_ * 2 - kRichImageSize) / 2,
+                         kRichImageSize, kRichImageSize);
+  x += kRichImageSize + HorizontalPadding();
   content_view_->SetBounds(x, y, width() - x, text_height_);
   y += text_height_;
   description_view_->SetBounds(x, y, width() - x, text_height_);
@@ -285,14 +290,15 @@
   x += icon_view_->width() + HorizontalPadding();
   int content_width = content_view_->CalculatePreferredSize().width();
   int description_width = description_view_->CalculatePreferredSize().width();
+  gfx::Size separator_size = separator_view_->CalculatePreferredSize();
   OmniboxPopupModel::ComputeMatchMaxWidths(
-      content_width, separator_view_->width(), description_width, width() - x,
+      content_width, separator_size.width(), description_width, width() - x,
       /*description_on_separate_line=*/false, !is_search_type_, &content_width,
       &description_width);
   content_view_->SetBounds(x, y, content_width, text_height_);
   if (description_width != 0) {
     x += content_view_->width();
-    separator_view_->SetSize(separator_view_->CalculatePreferredSize());
+    separator_view_->SetSize(separator_size);
     separator_view_->SetBounds(x, y, separator_view_->width(), text_height_);
     x += separator_view_->width();
     description_view_->SetBounds(x, y, description_width, text_height_);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index c6ae635..b61c371 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -43,10 +43,6 @@
 #include "ui/views/linux_ui/linux_ui.h"
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/ash_config.h"
-#endif
-
 namespace {
 
 enum PopupType { WIDE, NARROW, ROUNDED };
@@ -324,20 +320,6 @@
     loader.LoadExtension(path);
   }
 
-#if defined(OS_CHROMEOS)
-  if (chromeos::GetAshConfig() == ash::Config::MASH) {
-    // http://crbug.com/704942: Incognito frames do not update correctly in
-    // Mash, so the old values remain. Check here so this is revisited when
-    // fixed, and bail out of the rest of the test.
-    if (GetParam() == ROUNDED)
-      EXPECT_EQ(rounded_selection_color_dark, get_selection_color());
-    else
-      EXPECT_EQ(legacy_dark_color, get_selection_color());
-
-    return;
-  }
-#endif
-
   // Check the incognito browser first. Everything should now be light and never
   // GTK.
   if (GetParam() == ROUNDED) {
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index fbe5ade0..811c627 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -110,6 +110,7 @@
     content::PictureInPictureWindowController* controller)
     : controller_(controller),
       video_view_(new views::View()),
+      controls_background_view_(new views::View()),
       close_controls_view_(new views::ImageButton(nullptr)),
       play_pause_controls_view_(new views::ToggleImageButton(nullptr)) {
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
@@ -156,8 +157,7 @@
   // Determine the window size by fitting |natural_size_| within
   // |current_size_|, keeping to |natural_size_|'s aspect ratio.
   if (!natural_size_.IsEmpty())
-    current_size_ =
-        media::ScaleSizeToFitWithinTarget(natural_size_, current_size_);
+    UpdateCurrentSizeWithAspectRatio(current_size_);
 
   // The initial positioning is on the bottom right quadrant
   // of the primary display work area.
@@ -173,6 +173,14 @@
 }
 
 void OverlayWindowViews::SetUpViews() {
+  // Set up views::View that slightly darkens the video so the media controls
+  // appear more prominently. This is especially important in cases with a
+  // very light background.
+  controls_background_view_->SetSize(GetBounds().size());
+  controls_background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
+  GetControlsBackgroundLayer()->SetColor(SK_ColorBLACK);
+  GetControlsBackgroundLayer()->SetOpacity(0.2f);
+
   // Set up views::View that closes the window.
   close_controls_view_->SetSize(kCloseIconSize);
   close_controls_view_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
@@ -202,10 +210,40 @@
   play_pause_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
 
   // Don't show the controls until the mouse hovers over the window.
+  GetControlsBackgroundLayer()->SetVisible(false);
   GetCloseControlsLayer()->SetVisible(false);
   GetPlayPauseControlsLayer()->SetVisible(false);
 }
 
+void OverlayWindowViews::UpdateCurrentSizeWithAspectRatio(gfx::Size new_size) {
+  // This function will only be called once when this is true -- when the
+  // window is initially created.
+  if (current_size_.IsEmpty())
+    return;
+
+  // Check whether or not the new size and the video's natural size have the
+  // same orientation (landscape vs. portrait). Otherwise, the usage of the
+  // area checks below will flip the orientation of the video.
+  bool is_natural_size_landscape =
+      natural_size_.width() > natural_size_.height();
+  bool is_new_size_landscape = new_size.width() > new_size.height();
+
+  // TODO(apacible): Make resizing more strict. Currently, the window may
+  // resize to not adhere to the aspect ratio while the bounds are being
+  // dragged. When there is no more drag motion (e.g. mouse lifts), the window
+  // snaps to adhere to the aspect ratio. Ideally, the window will always
+  // adhere to the aspect ratio while in drag motion. http://crbug/829677.
+  if (is_new_size_landscape == is_natural_size_landscape) {
+    if (natural_size_.GetArea() > new_size.GetArea()) {
+      current_size_ =
+          media::ScaleSizeToEncompassTarget(natural_size_, new_size);
+    } else {
+      current_size_ =
+          media::ScaleSizeToFitWithinTarget(natural_size_, new_size);
+    }
+  }
+}
+
 bool OverlayWindowViews::IsActive() const {
   return views::Widget::IsActive();
 }
@@ -250,6 +288,10 @@
   return video_view_->layer();
 }
 
+ui::Layer* OverlayWindowViews::GetControlsBackgroundLayer() {
+  return controls_background_view_->layer();
+}
+
 ui::Layer* OverlayWindowViews::GetCloseControlsLayer() {
   return close_controls_view_->layer();
 }
@@ -292,11 +334,13 @@
   switch (event->type()) {
     // Only show the media controls when the mouse is hovering over the window.
     case ui::ET_MOUSE_ENTERED:
+      GetControlsBackgroundLayer()->SetVisible(true);
       GetCloseControlsLayer()->SetVisible(true);
       GetPlayPauseControlsLayer()->SetVisible(true);
       break;
 
     case ui::ET_MOUSE_EXITED:
+      GetControlsBackgroundLayer()->SetVisible(false);
       GetCloseControlsLayer()->SetVisible(false);
       GetPlayPauseControlsLayer()->SetVisible(false);
       break;
@@ -331,5 +375,7 @@
   if (controller_)
     controller_->UpdateLayerBounds();
 
-  views::Widget::OnNativeWidgetSizeChanged(new_size);
+  UpdateCurrentSizeWithAspectRatio(new_size);
+  SetBounds(gfx::Rect(GetBounds().origin(), current_size_));
+  views::Widget::OnNativeWidgetSizeChanged(current_size_);
 }
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index d753df0..0eff5ff 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -34,6 +34,7 @@
   gfx::Rect GetBounds() const override;
   void UpdateVideoSize(const gfx::Size& natural_size) override;
   ui::Layer* GetVideoLayer() override;
+  ui::Layer* GetControlsBackgroundLayer() override;
   ui::Layer* GetCloseControlsLayer() override;
   ui::Layer* GetPlayPauseControlsLayer() override;
   gfx::Rect GetCloseControlsBounds() override;
@@ -58,6 +59,10 @@
   // Set up the views::Views that will be shown on the window.
   void SetUpViews();
 
+  // Update |current_size_| closest to the |new_size| while adhering to the
+  // aspect ratio of the video, which is retrieved from |natural_size_|.
+  void UpdateCurrentSizeWithAspectRatio(gfx::Size new_size);
+
   // Not owned; |controller_| owns |this|.
   content::PictureInPictureWindowController* controller_;
 
@@ -77,6 +82,7 @@
 
   // Views to be shown.
   std::unique_ptr<views::View> video_view_;
+  std::unique_ptr<views::View> controls_background_view_;
   std::unique_ptr<views::ImageButton> close_controls_view_;
   std::unique_ptr<views::ToggleImageButton> play_pause_controls_view_;
 
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index dab1059..f206b4b8 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -184,7 +184,7 @@
   // time of first layout (nothing has loaded yet). Because of this, set it to.
   // total_dialog_height - header_height. On the other hand, the width will be
   // properly set so it can be 0 here.
-  web_view->SetPreferredSize(gfx::Size(0, kDialogHeight - 68));
+  web_view->SetPreferredSize(gfx::Size(0, kDialogHeight - 75));
   content_view->AddChildView(web_view.release());
 }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index d325dbd4..42d5872 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -191,12 +191,11 @@
   views::GridLayout* layout = container->SetLayoutManager(
       std::make_unique<views::GridLayout>(container));
 
-  constexpr int kHeaderTopVerticalInset = 14;
-  constexpr int kHeaderBottomVerticalInset = 8;
+  constexpr int kVerticalInset = 14;
   constexpr int kHeaderHorizontalInset = 16;
-  container->SetBorder(views::CreateEmptyBorder(
-      kHeaderTopVerticalInset, kHeaderHorizontalInset,
-      kHeaderBottomVerticalInset, kHeaderHorizontalInset));
+  container->SetBorder(
+      views::CreateEmptyBorder(kVerticalInset, kHeaderHorizontalInset,
+                               kVerticalInset, kHeaderHorizontalInset));
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
   // A column for the optional back arrow.
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 f5874f8..7091c8db 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -125,7 +125,7 @@
 }  // namespace
 
 class ProfileChooserViewExtensionsTest
-    : public SupportsTestDialog<ExtensionBrowserTest> {
+    : public SupportsTestDialog<extensions::ExtensionBrowserTest> {
  public:
   ProfileChooserViewExtensionsTest() {}
   ~ProfileChooserViewExtensionsTest() override {}
diff --git a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
index dd1ac5d..1191156 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension_browsertest.cc
@@ -93,7 +93,8 @@
   DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
 };
 
-class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
+class SelectFileDialogExtensionBrowserTest
+    : public extensions::ExtensionBrowserTest {
  public:
   enum DialogButtonType {
     DIALOG_BTN_OK,
@@ -116,11 +117,11 @@
     base::CreateDirectory(downloads_dir_);
 
     // Must run after our setup because it actually runs the test.
-    ExtensionBrowserTest::SetUp();
+    extensions::ExtensionBrowserTest::SetUp();
   }
 
   void TearDown() override {
-    ExtensionBrowserTest::TearDown();
+    extensions::ExtensionBrowserTest::TearDown();
 
     // Delete the dialog first, as it holds a pointer to the listener.
     dialog_ = NULL;
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 7fbe8795..c43aac9 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -49,6 +49,7 @@
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_analysis.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -434,6 +435,8 @@
   }
   OnButtonColorMaybeChanged();
   alert_indicator_button_->UpdateEnabledForMuteToggle();
+  if (MD::GetMode() == MD::MATERIAL_REFRESH)
+    RepaintSubsequentTab();
   Layout();
 }
 
@@ -931,6 +934,8 @@
 
 void Tab::OnMouseEntered(const ui::MouseEvent& event) {
   hover_controller_.Show(views::GlowHoverController::SUBTLE);
+  if (MD::GetMode() == MD::MATERIAL_REFRESH)
+    RepaintSubsequentTab();
   Layout();
 }
 
@@ -941,6 +946,8 @@
 
 void Tab::OnMouseExited(const ui::MouseEvent& event) {
   hover_controller_.Hide();
+  if (MD::GetMode() == MD::MATERIAL_REFRESH)
+    RepaintSubsequentTab();
   Layout();
 }
 
@@ -991,6 +998,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Tab, private
 
+void Tab::RepaintSubsequentTab() {
+  Tab* adjacent_tab = controller_->GetAdjacentTab(this, TabController::FORWARD);
+  if (adjacent_tab)
+    adjacent_tab->SchedulePaint();
+}
+
 void Tab::MaybeAdjustLeftForPinnedTab(gfx::Rect* bounds,
                                       int visual_width) const {
   if (ShouldRenderAsNormalTab())
@@ -1129,6 +1142,9 @@
       canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true);
     canvas->sk_canvas()->drawPicture(cache.stroke_record);
   }
+
+  if (!active)
+    PaintSeparator(canvas, inactive_color);
 }
 
 void Tab::PaintTabBackgroundFill(gfx::Canvas* canvas,
@@ -1188,6 +1204,29 @@
   canvas->DrawPath(path, flags);
 }
 
+void Tab::PaintSeparator(gfx::Canvas* canvas, SkColor inactive_color) {
+  if (MD::GetMode() != MD::MATERIAL_REFRESH || IsMouseHovered())
+    return;
+
+  // If the tab to the left is either active or the mouse is hovered over it,
+  // the separator on this tab should not be painted.
+  Tab* previous_tab =
+      controller_->GetAdjacentTab(this, TabController::BACKWARD);
+  if (previous_tab &&
+      (previous_tab->IsActive() || previous_tab->IsMouseHovered()))
+    return;
+
+  const int tab_height = GetContentsBounds().height();
+  gfx::RectF separator_bounds;
+  separator_bounds.set_size(gfx::SizeF(1, 16));
+  separator_bounds.set_origin(gfx::PointF(
+      (tab_height - separator_bounds.height()) / 2, GetTabEndcapWidth() / 2));
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setColor(color_utils::BlendTowardOppositeLuma(inactive_color, 0x5a));
+  canvas->DrawRect(separator_bounds, flags);
+}
+
 void Tab::UpdateIconVisibility() {
   center_favicon_ = false;
   showing_icon_ = showing_alert_indicator_ = showing_close_button_ = false;
@@ -1324,10 +1363,11 @@
     button_color_ = new_button_color;
     title_->SetEnabledColor(title_color);
     alert_indicator_button_->OnParentTabButtonColorChanged();
-    if (!MD::IsTouchOptimizedUiEnabled())
+    if (!MD::IsTouchOptimizedUiEnabled()) {
       close_button_->SetTabColor(button_color_,
                                  color_utils::IsDark(theme_provider->GetColor(
                                      ThemeProperties::COLOR_TOOLBAR)));
+    }
   }
   if (MD::IsTouchOptimizedUiEnabled())
     close_button_->ActiveStateChanged(this);
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index 0b885996..962d61a 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -216,6 +216,9 @@
   // ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
+  // Forces the tab to the right of this tab to repaint.
+  void RepaintSubsequentTab();
+
   // Invoked from Layout to adjust the position of the favicon or alert
   // indicator for pinned tabs. The visual_width parameter is how wide the
   // icon looks (rather than how wide the bounds are).
@@ -252,6 +255,10 @@
                                 bool active,
                                 SkColor color);
 
+  // Paints the separator line on the left edge of the tab if in material
+  // refresh mode. The painted color is derived from the inactive tab color.
+  void PaintSeparator(gfx::Canvas* canvas, SkColor inactive_color);
+
   // Computes which icons are visible in the tab. Should be called everytime
   // before layout is performed.
   void UpdateIconVisibility();
diff --git a/chrome/browser/ui/views/tabs/tab_controller.h b/chrome/browser/ui/views/tabs/tab_controller.h
index 326d6d05c..5fa2cdb 100644
--- a/chrome/browser/ui/views/tabs/tab_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_controller.h
@@ -29,17 +29,22 @@
 // Controller for tabs.
 class TabController {
  public:
+  // Used in GetAdjacentTab to indicate which adjacent tab to retrieve. FORWARD
+  // will return the adjacent tab to the right. BACKWARD will return the
+  // adjacent tab to the left of the given tab.
+  enum Direction { FORWARD, BACKWARD };
+
   virtual const ui::ListSelectionModel& GetSelectionModel() const = 0;
 
   // Returns true if multiple selection is supported.
   virtual bool SupportsMultipleSelection() = 0;
 
-  // Returns true if we should force the close buttons of the inactive tabs
-  // to be hidden.
+  // Returns true if the close buttons of the inactive tabs are forced to be
+  // hidden.
   virtual bool ShouldHideCloseButtonForInactiveTabs() = 0;
 
-  // Returns true if we should show the close button an inactive tab on mouse
-  // hover. This is predicated on ShouldHideCloseButtonForInactiveTabs()
+  // Returns true if the close button on an inactive tab should be shown on
+  // mouse hover. This is predicated on ShouldHideCloseButtonForInactiveTabs()
   // returning true.
   virtual bool ShouldShowCloseButtonOnHover() = 0;
 
@@ -100,6 +105,10 @@
   virtual Tab* GetTabAt(Tab* tab,
                         const gfx::Point& tab_in_tab_coordinates) = 0;
 
+  // Returns the next/previous tab in the model order. Returns nullptr if there
+  // isn't an adjacent tab in the given direction.
+  virtual Tab* GetAdjacentTab(Tab* tab, Direction direction) = 0;
+
   // Invoked when a mouse event occurs on |source|.
   virtual void OnMouseEventInTab(views::View* source,
                                  const ui::MouseEvent& event) = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 84845849..7b460a478 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -971,6 +971,15 @@
   return view && view->id() == VIEW_ID_TAB ? static_cast<Tab*>(view) : NULL;
 }
 
+Tab* TabStrip::GetAdjacentTab(Tab* tab, TabController::Direction direction) {
+  const int index = GetModelIndexOfTab(tab);
+  if (index < 0)
+    return nullptr;
+  const int new_index = index + (direction == TabController::FORWARD ? 1 : -1);
+  return new_index < 0 || new_index >= tab_count() ? nullptr
+                                                   : tab_at(new_index);
+}
+
 void TabStrip::OnMouseEventInTab(views::View* source,
                                  const ui::MouseEvent& event) {
   UpdateStackedLayoutFromMouseEvent(source, event);
@@ -1093,7 +1102,8 @@
   // ordering). Additionally we need to paint the tabs that are closing in
   // |tabs_closing_map_|.
   bool is_dragging = false;
-  Tab* active_tab = NULL;
+  Tab* active_tab = nullptr;
+  Tab* hovered_tab = nullptr;
   Tabs tabs_dragging;
   Tabs selected_tabs;
 
@@ -1119,8 +1129,15 @@
         }
       } else if (!tab->IsActive()) {
         if (!tab->IsSelected()) {
-          if (!stacked_layout_)
-            tab->Paint(paint_info);
+          if (!stacked_layout_) {
+            // In Refresh mode, defer the painting of the hovered tab to below.
+            if (MD::GetMode() == MD::MATERIAL_REFRESH &&
+                tab->IsMouseHovered()) {
+              hovered_tab = tab;
+            } else {
+              tab->Paint(paint_info);
+            }
+          }
         } else {
           selected_tabs.push_back(tab);
         }
@@ -1150,6 +1167,23 @@
   for (size_t i = 0; i < selected_tabs.size(); ++i)
     selected_tabs[i]->Paint(paint_info);
 
+  // If the last hovered tab is still animating and there is no currently
+  // hovered tab, make sure it still paints in the right order while it's
+  // animating.
+  if (!hovered_tab && last_hovered_tab_ &&
+      last_hovered_tab_->hover_controller()->ShouldDraw())
+    hovered_tab = last_hovered_tab_;
+
+  // The currently hovered tab or the last tab that was hovered should be
+  // painted right before the active tab to ensure the highlighted tab shape
+  // looks reasonable.
+  if (hovered_tab && !is_dragging)
+    hovered_tab->Paint(paint_info);
+
+  // Keep track of the last tab that was hovered to that it continues to be
+  // painted right before the active tab while the animation is running.
+  last_hovered_tab_ = hovered_tab;
+
   // Next comes the active tab.
   if (active_tab && !is_dragging)
     active_tab->Paint(paint_info);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 6ebf93c8..a5f5a05 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -248,6 +248,7 @@
   void ContinueDrag(views::View* view, const ui::LocatedEvent& event) override;
   bool EndDrag(EndDragReason reason) override;
   Tab* GetTabAt(Tab* tab, const gfx::Point& tab_in_tab_coordinates) override;
+  Tab* GetAdjacentTab(Tab* tab, TabController::Direction direction) override;
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override;
   bool ShouldPaintTab(
@@ -683,6 +684,10 @@
   // tab close comes from a touch device.
   base::OneShotTimer resize_layout_timer_;
 
+  // The last tab over which the mouse was hovered which may still have a hover
+  // animation in progress.
+  Tab* last_hovered_tab_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(TabStrip);
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index 8733877..8ff02ce 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -64,7 +64,10 @@
   }
   bool EndDrag(EndDragReason reason) override { return false; }
   Tab* GetTabAt(Tab* tab, const gfx::Point& tab_in_tab_coordinates) override {
-    return NULL;
+    return nullptr;
+  }
+  Tab* GetAdjacentTab(Tab* tab, TabController::Direction direction) override {
+    return nullptr;
   }
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override {}
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
index 35869c803..1dba9cd27 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
@@ -134,12 +134,13 @@
 
 }  // namespace
 
-class ToolbarActionViewInteractiveUITest : public ExtensionBrowserTest {
+class ToolbarActionViewInteractiveUITest
+    : public extensions::ExtensionBrowserTest {
  protected:
   ToolbarActionViewInteractiveUITest();
   ~ToolbarActionViewInteractiveUITest() override;
 
-  // ExtensionBrowserTest:
+  // extensions::ExtensionBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override;
   void TearDownOnMainThread() override;
 
@@ -154,7 +155,7 @@
 
 void ToolbarActionViewInteractiveUITest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   ToolbarActionsBar::disable_animations_for_testing_ = true;
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
index 0be5de4..6f92356 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -38,7 +38,7 @@
 
 using bookmarks::BookmarkModel;
 
-class ToolbarViewInteractiveUITest : public ExtensionBrowserTest {
+class ToolbarViewInteractiveUITest : public extensions::ExtensionBrowserTest {
  public:
   ToolbarViewInteractiveUITest();
   ~ToolbarViewInteractiveUITest() override;
@@ -135,13 +135,13 @@
 
 void ToolbarViewInteractiveUITest::SetUpCommandLine(
     base::CommandLine* command_line) {
-  ExtensionBrowserTest::SetUpCommandLine(command_line);
+  extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
   ToolbarActionsBar::disable_animations_for_testing_ = true;
   BrowserAppMenuButton::g_open_app_immediately_for_testing = true;
 }
 
 void ToolbarViewInteractiveUITest::SetUpOnMainThread() {
-  ExtensionBrowserTest::SetUpOnMainThread();
+  extensions::ExtensionBrowserTest::SetUpOnMainThread();
   ExtensionToolbarMenuView::set_close_menu_delay_for_testing(0);
 
   toolbar_view_ = BrowserView::GetBrowserViewForBrowser(browser())->toolbar();
diff --git a/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
index 7c863b5..c317f4f 100644
--- a/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
@@ -107,7 +107,8 @@
       DCHECK(LoginDisplayHost::default_host());
       LoginDisplayHost::default_host()->SetDisplayAndGivenName(
           account_info.display_name(), account_info.given_name());
-      UserContext user_context(account_id);
+      UserContext user_context(
+          user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY, account_id);
       user_context.SetKey(key);
       user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
       user_context.SetIsUsingOAuth(false);
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 479ab976..111345d 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -243,6 +243,16 @@
   return is_child ? user_manager::USER_TYPE_CHILD
                   : user_manager::USER_TYPE_REGULAR;
 }
+
+user_manager::UserType CalculateUserType(const AccountId& account_id) {
+  if (user_manager::UserManager::Get()->IsSupervisedAccountId(account_id))
+    return user_manager::USER_TYPE_SUPERVISED;
+
+  if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
+    return user_manager::USER_TYPE_ACTIVE_DIRECTORY;
+
+  return user_manager::USER_TYPE_REGULAR;
+}
 }  // namespace
 
 // A class that's used to specify the way how Gaia should be loaded.
@@ -659,7 +669,8 @@
           username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY));
       LoginDisplayHost::default_host()->SetDisplayAndGivenName(
           account_info.display_name(), account_info.given_name());
-      UserContext user_context(account_id);
+      UserContext user_context(
+          user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY, account_id);
       user_context.SetKey(key);
       user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
       user_context.SetIsUsingOAuth(false);
@@ -840,8 +851,14 @@
   DCHECK(!gaia_id.empty());
   const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
   LoginDisplayHost::default_host()->SetDisplayEmail(sanitized_email);
-  UserContext user_context(
-      GetAccountId(typed_email, gaia_id, AccountType::GOOGLE));
+  const AccountId account_id =
+      GetAccountId(typed_email, gaia_id, AccountType::GOOGLE);
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+
+  UserContext user_context =
+      user ? UserContext(*user)
+           : UserContext(CalculateUserType(account_id), account_id);
   user_context.SetKey(Key(password));
   user_context.SetAuthFlow(using_saml
                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 02b21f3..bc51016 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -1215,16 +1215,18 @@
   const user_manager::User* user =
       user_manager::UserManager::Get()->FindUser(account_id);
   DCHECK(user);
-  user_manager::UserType user_type = user_manager::UserType::USER_TYPE_REGULAR;
+  UserContext user_context;
   if (!user) {
     LOG(ERROR) << "HandleAuthenticateUser: User not found! account type="
                << AccountId::AccountTypeToString(account_id.GetAccountType());
-    if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
-      user_type = user_manager::USER_TYPE_ACTIVE_DIRECTORY;
+    const user_manager::UserType user_type =
+        (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
+            ? user_manager::USER_TYPE_ACTIVE_DIRECTORY
+            : user_manager::UserType::USER_TYPE_REGULAR;
+    user_context = UserContext(user_type, account_id);
   } else {
-    user_type = user->GetType();
+    user_context = UserContext(*user);
   }
-  UserContext user_context(user_type, account_id);
   user_context.SetKey(Key(password));
   // Only save the password for enterprise users. See https://crbug.com/386606.
   const bool is_enterprise_managed = g_browser_process->platform_part()
diff --git a/chrome/browser/ui/webui/net_export_ui.cc b/chrome/browser/ui/webui/net_export_ui.cc
index 56a0086..0aac4e4a 100644
--- a/chrome/browser/ui/webui/net_export_ui.cc
+++ b/chrome/browser/ui/webui/net_export_ui.cc
@@ -137,10 +137,6 @@
   // NetLog file.
   void ShowSelectFileDialog(const base::FilePath& default_path);
 
-  // Returns a list of context getters used to retrieve ongoing events when
-  // logging starts so that net log entries can be added for those events.
-  URLRequestContextGetterList GetURLRequestContexts() const;
-
   // Cache of g_browser_process->net_log()->net_export_file_writer(). This
   // is owned by ChromeNetLog which is owned by BrowserProcessImpl.
   net_log::NetExportFileWriter* file_writer_;
@@ -167,8 +163,7 @@
     : file_writer_(g_browser_process->net_log()->net_export_file_writer()),
       state_observer_manager_(this),
       weak_ptr_factory_(this) {
-  file_writer_->Initialize(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+  file_writer_->Initialize();
 }
 
 NetExportMessageHandler::~NetExportMessageHandler() {
@@ -177,7 +172,7 @@
   if (select_file_dialog_)
     select_file_dialog_->ListenerDestroyed();
 
-  file_writer_->StopNetLog(nullptr, nullptr);
+  file_writer_->StopNetLog(nullptr);
 }
 
 void NetExportMessageHandler::RegisterMessages() {
@@ -270,8 +265,7 @@
                chrome_browser_net::GetWindowsServiceProviders());
 #endif
 
-  file_writer_->StopNetLog(std::move(ui_thread_polled_data),
-                           Profile::FromWebUI(web_ui())->GetRequestContext());
+  file_writer_->StopNetLog(std::move(ui_thread_polled_data));
 }
 
 void NetExportMessageHandler::OnSendNetLog(const base::ListValue* list) {
@@ -334,7 +328,10 @@
   file_writer_->StartNetLog(
       path, capture_mode_, max_log_file_size_,
       base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
-      chrome::GetChannelName(), GetURLRequestContexts());
+      chrome::GetChannelName(),
+      content::BrowserContext::GetDefaultStoragePartition(
+          Profile::FromWebUI(web_ui()))
+          ->GetNetworkContext());
 }
 
 void NetExportMessageHandler::ShowFileInShell(const base::FilePath& path) {
@@ -384,24 +381,6 @@
       &file_type_info, 0, base::FilePath::StringType(), owning_window, nullptr);
 }
 
-NetExportMessageHandler::URLRequestContextGetterList
-NetExportMessageHandler::GetURLRequestContexts() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  URLRequestContextGetterList context_getters;
-
-  Profile* profile = Profile::FromWebUI(web_ui());
-
-  context_getters.push_back(profile->GetRequestContext());
-  context_getters.push_back(
-      content::BrowserContext::GetDefaultStoragePartition(profile)
-          ->GetMediaURLRequestContext());
-  context_getters.push_back(
-      g_browser_process->io_thread()->system_url_request_context_getter());
-
-  return context_getters;
-}
-
 }  // namespace
 
 NetExportUI::NetExportUI(content::WebUI* web_ui) : WebUIController(web_ui) {
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index c2893076..12c2eb2 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -64,7 +64,6 @@
 #include "net/http/http_cache.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_network_session.h"
-#include "net/http/http_server_properties.h"
 #include "net/http/http_stream_factory.h"
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log.h"
diff --git a/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc b/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
index eeddd63..57ba15e1 100644
--- a/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
@@ -679,12 +679,19 @@
 IN_PROC_BROWSER_TEST_F(PolicyToolUITest, RenameSessionWithExistingSessionName) {
   CreateMultipleSessionFiles(3);
   ui_test_utils::NavigateToURL(browser(), GURL("chrome://policy-tool"));
+  content::RunAllTasksUntilIdle();
+  // Make sure the current session is '2'.
   EXPECT_EQ("2", ExtractSinglePolicyValue("SessionId"));
 
   // Check that a session can not be renamed with a name of another existing
   // session.
   RenameSession("2", "1");
+  // Wait until the posted tasks are done. After check that name already exist
+  // the tool displays an error message in the UI.
+  content::RunAllTasksUntilIdle();
   EXPECT_TRUE(IsSessionRenameErrorMessageDisplayed());
+
+  // Check the names that appear in the session list didn't change.
   base::ListValue expected;
   expected.GetList().push_back(base::Value("2"));
   expected.GetList().push_back(base::Value("1"));
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 8a9836d..6f58ae3 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -107,6 +107,7 @@
   PRINT_WITH_CLOUD_PRINT,
   PRINT_WITH_PRIVET,
   PRINT_WITH_EXTENSION,
+  OPEN_IN_MAC_PREVIEW,
   USERACTION_BUCKET_BOUNDARY
 };
 
@@ -144,19 +145,89 @@
   PRINT_DOCUMENT_TYPE_BUCKET_BOUNDARY
 };
 
-void ReportUserActionHistogram(enum UserActionBuckets event) {
+void ReportUserActionHistogram(UserActionBuckets event) {
   UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event,
                             USERACTION_BUCKET_BOUNDARY);
 }
 
-void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) {
+void ReportPrintSettingHistogram(PrintSettingsBuckets setting) {
   UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting,
                             PRINT_SETTINGS_BUCKET_BOUNDARY);
 }
 
-void ReportPrintDocumentTypeHistogram(enum PrintDocumentTypeBuckets doctype) {
+void ReportPrintDocumentTypeAndSizeHistograms(PrintDocumentTypeBuckets doctype,
+                                              size_t average_page_size_in_kb) {
   UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintDocumentType", doctype,
                             PRINT_DOCUMENT_TYPE_BUCKET_BOUNDARY);
+  switch (doctype) {
+    case HTML_DOCUMENT:
+      UMA_HISTOGRAM_MEMORY_KB("PrintPreview.PrintDocumentSize.HTML",
+                              average_page_size_in_kb);
+      break;
+    case PDF_DOCUMENT:
+      UMA_HISTOGRAM_MEMORY_KB("PrintPreview.PrintDocumentSize.PDF",
+                              average_page_size_in_kb);
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+bool ReportPageCountHistogram(UserActionBuckets user_action, int page_count) {
+  switch (user_action) {
+    case PRINT_TO_PRINTER:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
+      return true;
+    case PRINT_TO_PDF:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count);
+      return true;
+    case FALLBACK_TO_ADVANCED_SETTINGS_DIALOG:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", page_count);
+      return true;
+    case PRINT_WITH_CLOUD_PRINT:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint",
+                           page_count);
+      return true;
+    case PRINT_WITH_PRIVET:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet",
+                           page_count);
+      return true;
+    case PRINT_WITH_EXTENSION:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithExtension",
+                           page_count);
+      return true;
+    case OPEN_IN_MAC_PREVIEW:
+      UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.OpenInMacPreview",
+                           page_count);
+      return true;
+    default:
+      return false;
+  }
+}
+
+PrinterType GetPrinterTypeForUserAction(UserActionBuckets user_action) {
+  switch (user_action) {
+    case PRINT_WITH_PRIVET:
+      return PrinterType::kPrivetPrinter;
+    case PRINT_WITH_EXTENSION:
+      return PrinterType::kExtensionPrinter;
+    case PRINT_TO_PDF:
+      return PrinterType::kPdfPrinter;
+    case PRINT_TO_PRINTER:
+    case FALLBACK_TO_ADVANCED_SETTINGS_DIALOG:
+    case OPEN_IN_MAC_PREVIEW:
+      return PrinterType::kLocalPrinter;
+    default:
+      NOTREACHED();
+      return PrinterType::kLocalPrinter;
+  }
+}
+
+base::Value GetErrorValue(UserActionBuckets user_action,
+                          base::StringPiece description) {
+  return user_action == PRINT_WITH_PRIVET ? base::Value(-1)
+                                          : base::Value(description);
 }
 
 // Dictionary Fields for Print Preview initial settings. Keep in sync with
@@ -299,6 +370,30 @@
   }
 }
 
+UserActionBuckets DetermineUserAction(const base::DictionaryValue& settings) {
+  bool value = false;
+#if defined(OS_MACOSX)
+  value = settings.HasKey(printing::kSettingOpenPDFInPreview);
+#endif
+  if (value)
+    return OPEN_IN_MAC_PREVIEW;
+  if (settings.HasKey(printing::kSettingCloudPrintId))
+    return PRINT_WITH_CLOUD_PRINT;
+  settings.GetBoolean(printing::kSettingPrintWithPrivet, &value);
+  if (value)
+    return PRINT_WITH_PRIVET;
+  settings.GetBoolean(printing::kSettingPrintWithExtension, &value);
+  if (value)
+    return PRINT_WITH_EXTENSION;
+  settings.GetBoolean(printing::kSettingPrintToPDF, &value);
+  if (value)
+    return PRINT_TO_PDF;
+  settings.GetBoolean(printing::kSettingShowSystemDialog, &value);
+  if (value)
+    return FALLBACK_TO_ADVANCED_SETTINGS_DIALOG;
+  return PRINT_TO_PRINTER;
+}
+
 base::LazyInstance<printing::StickySettings>::DestructorAtExit
     g_sticky_settings = LAZY_INSTANCE_INITIALIZER;
 
@@ -640,109 +735,71 @@
     return;
   }
 
-  ReportPrintSettingsStats(*settings);
-
-  // Report whether the user printed a PDF or an HTML document.
-  ReportPrintDocumentTypeHistogram(print_preview_ui()->source_is_modifiable() ?
-                                   HTML_DOCUMENT : PDF_DOCUMENT);
-
-  bool print_to_pdf = false;
-  bool is_cloud_printer = false;
-  bool print_with_privet = false;
-  bool print_with_extension = false;
-  bool show_system_dialog = false;
-  bool open_pdf_in_preview = false;
-#if defined(OS_MACOSX)
-  open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview);
-#endif
-
-  if (!open_pdf_in_preview) {
-    settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
-    settings->GetBoolean(printing::kSettingPrintWithPrivet, &print_with_privet);
-    settings->GetBoolean(printing::kSettingPrintWithExtension,
-                         &print_with_extension);
-    settings->GetBoolean(printing::kSettingShowSystemDialog,
-                         &show_system_dialog);
-    is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId);
-  }
+  const UserActionBuckets user_action = DetermineUserAction(*settings);
 
   int page_count = 0;
   if (!settings->GetInteger(printing::kSettingPreviewPageCount, &page_count) ||
       page_count <= 0) {
-    RejectJavascriptCallback(base::Value(callback_id), base::Value(-1));
+    RejectJavascriptCallback(base::Value(callback_id),
+                             GetErrorValue(user_action, "NO_PAGE_COUNT"));
     return;
   }
 
-  if (print_with_privet) {
-#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet", page_count);
-    ReportUserActionHistogram(PRINT_WITH_PRIVET);
-#endif
-  } else if (print_with_extension) {
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithExtension",
-                         page_count);
-    ReportUserActionHistogram(PRINT_WITH_EXTENSION);
-  } else if (print_to_pdf) {
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count);
-    ReportUserActionHistogram(PRINT_TO_PDF);
-  } else if (show_system_dialog) {
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", page_count);
-    ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
-  } else if (!open_pdf_in_preview) {
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
-    ReportUserActionHistogram(PRINT_TO_PRINTER);
-  } else if (is_cloud_printer) {
-    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint",
-                         page_count);
-    ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT);
-  }
-
   scoped_refptr<base::RefCountedMemory> data;
   print_preview_ui()->GetPrintPreviewDataForIndex(
       printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data);
   if (!data) {
     // Nothing to print, no preview available.
-    RejectJavascriptCallback(
-        base::Value(callback_id),
-        print_with_privet ? base::Value(-1) : base::Value("NO_DATA"));
+    RejectJavascriptCallback(base::Value(callback_id),
+                             GetErrorValue(user_action, "NO_DATA"));
     return;
   }
   DCHECK(data->size());
   DCHECK(data->front());
 
-  if (is_cloud_printer) {
-    // Does not send the title like the other printer handler types below,
-    // because JS already has the document title from the initial settings.
-    SendCloudPrintJob(callback_id, data.get());
-    return;
-  }
-
   std::string destination_id;
   std::string print_ticket;
   std::string capabilities;
   int width = 0;
   int height = 0;
-  if ((print_with_privet || print_with_extension) &&
-      (!settings->GetString(printing::kSettingDeviceName, &destination_id) ||
-       !settings->GetString(printing::kSettingTicket, &print_ticket) ||
-       !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
-       !settings->GetInteger(printing::kSettingPageWidth, &width) ||
-       !settings->GetInteger(printing::kSettingPageHeight, &height) ||
-       width <= 0 || height <= 0)) {
+  if (user_action == PRINT_WITH_PRIVET || user_action == PRINT_WITH_EXTENSION) {
+    if (!settings->GetString(printing::kSettingDeviceName, &destination_id) ||
+        !settings->GetString(printing::kSettingTicket, &print_ticket) ||
+        !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
+        !settings->GetInteger(printing::kSettingPageWidth, &width) ||
+        !settings->GetInteger(printing::kSettingPageHeight, &height) ||
+        width <= 0 || height <= 0) {
+      NOTREACHED();
+      RejectJavascriptCallback(base::Value(callback_id),
+                               GetErrorValue(user_action, "FAILED"));
+      return;
+    }
+  }
+
+  // After validating |settings|, record metrics.
+  ReportPrintSettingsStats(*settings);
+  {
+    PrintDocumentTypeBuckets doc_type =
+        print_preview_ui()->source_is_modifiable() ? HTML_DOCUMENT
+                                                   : PDF_DOCUMENT;
+    size_t average_page_size_in_kb = data->size() / page_count;
+    average_page_size_in_kb /= 1024;
+    ReportPrintDocumentTypeAndSizeHistograms(doc_type, average_page_size_in_kb);
+  }
+  ReportUserActionHistogram(user_action);
+  if (!ReportPageCountHistogram(user_action, page_count)) {
     NOTREACHED();
-    RejectJavascriptCallback(
-        base::Value(callback_id),
-        print_with_privet ? base::Value(-1) : base::Value("FAILED"));
     return;
   }
 
-  PrinterType type = PrinterType::kLocalPrinter;
-  if (print_with_extension)
-    type = PrinterType::kExtensionPrinter;
-  else if (print_with_privet)
-    type = PrinterType::kPrivetPrinter;
-  else if (print_to_pdf)
-    type = PrinterType::kPdfPrinter;
+  if (user_action == PRINT_WITH_CLOUD_PRINT) {
+    // Does not send the title like the other printer handler types below,
+    // because JS already has the document title from the initial settings.
+    SendCloudPrintJob(callback_id, data.get());
+    return;
+  }
+
+  PrinterType type = GetPrinterTypeForUserAction(user_action);
   PrinterHandler* handler = GetPrinterHandler(type);
   handler->StartPrint(
       destination_id, capabilities, print_preview_ui()->initiator_title(),
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 8332032..92db0fd 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -112,6 +112,21 @@
                   callback);
 }
 
+// Returns the list of |printers| formatted as a CupsPrintersList.
+base::Value BuildCupsPrintersList(const std::vector<Printer>& printers) {
+  base::Value printers_list(base::Value::Type::LIST);
+  for (const Printer& printer : printers) {
+    // Some of these printers could be invalid but we want to allow the user
+    // to edit them. crbug.com/778383
+    printers_list.GetList().push_back(
+        base::Value::FromUniquePtrValue(GetCupsPrinterInfo(printer)));
+  }
+
+  base::Value response(base::Value::Type::DICTIONARY);
+  response.SetKey("printerList", std::move(printers_list));
+  return response;
+}
+
 // Extracts a sanitized value of printerQueue from |printer_dict|.  Returns an
 // empty string if the value was not present in the dictionary.
 std::string GetPrinterQueue(const base::DictionaryValue& printer_dict) {
@@ -275,16 +290,8 @@
   std::vector<Printer> printers =
       printers_manager_->GetPrinters(CupsPrintersManager::kConfigured);
 
-  auto printers_list = std::make_unique<base::ListValue>();
-  for (const Printer& printer : printers) {
-    // Some of these printers could be invalid but we want to allow the user
-    // to edit them. crbug.com/778383
-    printers_list->Append(GetCupsPrinterInfo(printer));
-  }
-
-  auto response = std::make_unique<base::DictionaryValue>();
-  response->Set("printerList", std::move(printers_list));
-  ResolveJavascriptCallback(base::Value(callback_id), *response);
+  auto response = BuildCupsPrintersList(printers);
+  ResolveJavascriptCallback(base::Value(callback_id), response);
 }
 
 void CupsPrintersHandler::HandleUpdateCupsPrinter(const base::ListValue* args) {
@@ -753,6 +760,8 @@
   UMA_HISTOGRAM_COUNTS_100(
       "Printing.CUPS.PrintersDiscovered",
       discovered_printers_.size() + automatic_printers_.size());
+  // Scan completes immediately right now.  Emit done.
+  FireWebUIListener("on-printer-discovery-done");
 }
 
 void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) {
@@ -780,20 +789,32 @@
 void CupsPrintersHandler::OnPrintersChanged(
     CupsPrintersManager::PrinterClass printer_class,
     const std::vector<Printer>& printers) {
-  if (!discovery_active_) {
-    return;
-  }
   switch (printer_class) {
     case CupsPrintersManager::kAutomatic:
       automatic_printers_ = printers;
+      UpdateDiscoveredPrinters();
       break;
     case CupsPrintersManager::kDiscovered:
       discovered_printers_ = printers;
+      UpdateDiscoveredPrinters();
       break;
-    default:
-      // It's a class we don't care about.
+    case CupsPrintersManager::kConfigured: {
+      auto printers_list = BuildCupsPrintersList(printers);
+      FireWebUIListener("on-printers-changed", printers_list);
+      break;
+    }
+    case CupsPrintersManager::kEnterprise:
+    case CupsPrintersManager::kNumPrinterClasses:
+      // These classes are not shown.
       return;
   }
+}
+
+void CupsPrintersHandler::UpdateDiscoveredPrinters() {
+  if (!discovery_active_) {
+    return;
+  }
+
   std::unique_ptr<base::ListValue> printers_list =
       std::make_unique<base::ListValue>();
   for (const Printer& printer : automatic_printers_) {
@@ -804,7 +825,6 @@
   }
 
   FireWebUIListener("on-printer-discovered", *printers_list);
-  FireWebUIListener("on-printer-discovery-done");
 }
 
 void CupsPrintersHandler::HandleAddDiscoveredPrinter(
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index dddfb63e..447eecb 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -126,6 +126,9 @@
       const std::string& manufacturer,
       const std::string& model);
 
+  // Emits the updated discovered printer list after new printers are received.
+  void UpdateDiscoveredPrinters();
+
   // Attempt to add a discovered printer.
   void HandleAddDiscoveredPrinter(const base::ListValue* args);
 
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index ff81d2c9..d78d3a3 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/net/url_request_mock_util.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -962,6 +963,35 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FastUnloadTest,
+                       BrowserListForceCloseWithIncognitoProfileCloseProfiles) {
+  Profile* otr_profile = browser()->profile()->GetOffTheRecordProfile();
+  Browser* otr_browser1 = CreateBrowser(otr_profile);
+  Browser* otr_browser2 = CreateBrowser(otr_profile);
+  content::WindowedNotificationObserver otr_browser1_observer(
+      chrome::NOTIFICATION_BROWSER_CLOSED,
+      content::Source<Browser>(otr_browser1));
+  content::WindowedNotificationObserver otr_browser2_observer(
+      chrome::NOTIFICATION_BROWSER_CLOSED,
+      content::Source<Browser>(otr_browser2));
+  UnloadResults unload_results;
+  BrowserList::CloseAllBrowsersWithIncognitoProfile(
+      otr_profile,
+      base::BindRepeating(&UnloadResults::AddSuccess,
+                          base::Unretained(&unload_results)),
+      base::BindRepeating(&UnloadResults::AddAbort,
+                          base::Unretained(&unload_results)),
+      true);
+  // Incognito browsers should be closed.
+  otr_browser1_observer.Wait();
+  otr_browser2_observer.Wait();
+  // Browser with original profile should not close.
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+  EXPECT_EQ(1, unload_results.get_successes());
+  EXPECT_EQ(0, unload_results.get_aborts());
+}
+
+IN_PROC_BROWSER_TEST_F(FastUnloadTest,
                        BrowserListForceCloseAfterNormalCloseWithFastUnload) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index ca5b9f5..72e907b 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -77,10 +77,6 @@
     "elements/paged_grid_layout.h",
     "elements/paged_scroll_view.cc",
     "elements/paged_scroll_view.h",
-    "elements/prompt.cc",
-    "elements/prompt.h",
-    "elements/prompt_texture.cc",
-    "elements/prompt_texture.h",
     "elements/rect.cc",
     "elements/rect.h",
     "elements/render_text_wrapper.cc",
@@ -99,6 +95,8 @@
     "elements/spinner.h",
     "elements/text.cc",
     "elements/text.h",
+    "elements/text_button.cc",
+    "elements/text_button.h",
     "elements/text_input.cc",
     "elements/text_input.h",
     "elements/textured_element.cc",
@@ -277,7 +275,6 @@
     "elements/omnibox_text_field_unittest.cc",
     "elements/oval_unittest.cc",
     "elements/paged_grid_layout_unittest.cc",
-    "elements/prompt_unittest.cc",
     "elements/rect_unittest.cc",
     "elements/repositioner_unittest.cc",
     "elements/resizer_unittest.cc",
diff --git a/chrome/browser/vr/browser_ui_interface.h b/chrome/browser/vr/browser_ui_interface.h
index b0b666f0..0211aaf 100644
--- a/chrome/browser/vr/browser_ui_interface.h
+++ b/chrome/browser/vr/browser_ui_interface.h
@@ -56,6 +56,7 @@
                               const base::string16& title) = 0;
   virtual void RemoveTab(int id, bool incognito) = 0;
   virtual void RemoveAllTabs() = 0;
+  virtual void OnTabSelected(int id, bool incognito) = 0;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/elements/button.cc b/chrome/browser/vr/elements/button.cc
index 0a867861..c99bf772 100644
--- a/chrome/browser/vr/elements/button.cc
+++ b/chrome/browser/vr/elements/button.cc
@@ -108,10 +108,14 @@
     click_handler_.Run();
 }
 
+void Button::OnSetColors(const ButtonColors& colors) {}
+
 void Button::OnStateUpdated() {
   pressed_ = hovered_ ? down_ : false;
   background_->SetColor(colors_.GetBackgroundColor(hovered_, pressed_));
 
+  OnSetColors(colors_);
+
   if (hover_offset_ == 0.0f)
     return;
 
@@ -136,7 +140,9 @@
 }
 
 void Button::OnSetSize(const gfx::SizeF& size) {
-  background_->SetSize(size.width(), size.height());
+  if (!background_->contributes_to_parent_bounds()) {
+    background_->SetSize(size.width(), size.height());
+  }
   hit_plane_->SetSize(size.width(), size.height());
 }
 
diff --git a/chrome/browser/vr/elements/button.h b/chrome/browser/vr/elements/button.h
index 02a4da14..46b98dc 100644
--- a/chrome/browser/vr/elements/button.h
+++ b/chrome/browser/vr/elements/button.h
@@ -76,6 +76,8 @@
   void HandleButtonDown();
   void HandleButtonUp();
 
+  virtual void OnSetColors(const ButtonColors& colors);
+
   const Sounds& GetSounds() const override;
 
   bool down_ = false;
diff --git a/chrome/browser/vr/elements/prompt.cc b/chrome/browser/vr/elements/prompt.cc
deleted file mode 100644
index c7d1874..0000000
--- a/chrome/browser/vr/elements/prompt.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/vr/elements/prompt.h"
-
-#include "chrome/browser/vr/elements/prompt_texture.h"
-
-namespace vr {
-
-Prompt::Prompt(int preferred_width,
-               int content_message_id,
-               const gfx::VectorIcon& icon,
-               int primary_button_message_id,
-               int secondary_button_message_id,
-               const PromptCallback& result_callback)
-    : TexturedElement(),
-      texture_(std::make_unique<PromptTexture>(content_message_id,
-                                               icon,
-                                               primary_button_message_id,
-                                               secondary_button_message_id)),
-      preferred_width_(preferred_width),
-      result_callback_(result_callback) {}
-
-Prompt::~Prompt() = default;
-
-void Prompt::OnHoverEnter(const gfx::PointF& position) {
-  OnStateUpdated(position);
-}
-
-void Prompt::OnHoverLeave() {
-  OnStateUpdated(gfx::PointF(std::numeric_limits<float>::max(),
-                             std::numeric_limits<float>::max()));
-}
-
-void Prompt::OnMove(const gfx::PointF& position) {
-  OnStateUpdated(position);
-}
-
-void Prompt::OnButtonDown(const gfx::PointF& position) {
-  if (texture_->HitsPrimaryButton(position))
-    primary_down_ = true;
-  else if (texture_->HitsSecondaryButton(position))
-    secondary_down_ = true;
-  OnStateUpdated(position);
-}
-
-void Prompt::OnButtonUp(const gfx::PointF& position) {
-  if (primary_down_ && texture_->HitsPrimaryButton(position))
-    result_callback_.Run(PRIMARY, reason_);
-  else if (secondary_down_ && texture_->HitsSecondaryButton(position))
-    result_callback_.Run(SECONDARY, reason_);
-
-  primary_down_ = false;
-  secondary_down_ = false;
-
-  OnStateUpdated(position);
-}
-
-void Prompt::SetContentMessageId(int message_id) {
-  texture_->SetContentMessageId(message_id);
-}
-
-void Prompt::SetIconColor(SkColor color) {
-  static_cast<PromptTexture*>(GetTexture())->SetIconColor(color);
-}
-
-void Prompt::SetPrimaryButtonColors(const ButtonColors& colors) {
-  texture_->SetPrimaryButtonColors(colors);
-}
-
-void Prompt::SetSecondaryButtonColors(const ButtonColors& colors) {
-  texture_->SetSecondaryButtonColors(colors);
-}
-
-void Prompt::SetTextureForTesting(std::unique_ptr<PromptTexture> texture) {
-  texture_ = std::move(texture);
-}
-
-void Prompt::ClickPrimaryButtonForTesting() {
-  result_callback_.Run(PRIMARY, reason_);
-}
-
-void Prompt::ClickSecondaryButtonForTesting() {
-  result_callback_.Run(SECONDARY, reason_);
-}
-
-void Prompt::Cancel() {
-  result_callback_.Run(NONE, reason_);
-}
-
-void Prompt::OnStateUpdated(const gfx::PointF& position) {
-  const bool primary_hovered = texture_->HitsPrimaryButton(position);
-  const bool secondary_hovered = texture_->HitsSecondaryButton(position);
-
-  texture_->SetPrimaryButtonHovered(primary_hovered);
-  texture_->SetPrimaryButtonPressed(primary_hovered ? primary_down_ : false);
-  texture_->SetSecondaryButtonHovered(secondary_hovered);
-  texture_->SetSecondaryButtonPressed(secondary_hovered ? secondary_down_
-                                                        : false);
-}
-
-UiTexture* Prompt::GetTexture() const {
-  return texture_.get();
-}
-
-bool Prompt::TextureDependsOnMeasurement() const {
-  return false;
-}
-
-gfx::Size Prompt::MeasureTextureSize() {
-  return texture_->GetPreferredTextureSize(preferred_width_);
-}
-
-}  // namespace vr
diff --git a/chrome/browser/vr/elements/prompt.h b/chrome/browser/vr/elements/prompt.h
deleted file mode 100644
index d2f24071..0000000
--- a/chrome/browser/vr/elements/prompt.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_VR_ELEMENTS_PROMPT_H_
-#define CHROME_BROWSER_VR_ELEMENTS_PROMPT_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "chrome/browser/vr/elements/textured_element.h"
-#include "chrome/browser/vr/ui_unsupported_mode.h"
-#include "ui/gfx/vector_icon_types.h"
-
-namespace vr {
-
-class PromptTexture;
-struct ButtonColors;
-
-class Prompt : public TexturedElement {
- public:
-  enum Button { NONE, PRIMARY, SECONDARY };
-
-  typedef typename base::RepeatingCallback<void(Button, UiUnsupportedMode)>
-      PromptCallback;
-
-  Prompt(int preferred_width,
-         int content_message_id,
-         const gfx::VectorIcon& icon,
-         int primary_button_message_id,
-         int secondary_button_message_id,
-         const PromptCallback& result_callback);
-  ~Prompt() override;
-
-  void set_reason(UiUnsupportedMode reason) { reason_ = reason; }
-
-  void OnHoverEnter(const gfx::PointF& position) override;
-  void OnHoverLeave() override;
-  void OnMove(const gfx::PointF& position) override;
-  void OnButtonDown(const gfx::PointF& position) override;
-  void OnButtonUp(const gfx::PointF& position) override;
-
-  void SetContentMessageId(int message_id);
-  void SetIconColor(SkColor color);
-  void SetPrimaryButtonColors(const ButtonColors& colors);
-  void SetSecondaryButtonColors(const ButtonColors& colors);
-
-  void Cancel();
-
-  void SetTextureForTesting(std::unique_ptr<PromptTexture> texture);
-  void ClickPrimaryButtonForTesting();
-  void ClickSecondaryButtonForTesting();
-
- protected:
-  UiTexture* GetTexture() const override;
-
- private:
-  void OnStateUpdated(const gfx::PointF& position);
-
-  bool TextureDependsOnMeasurement() const override;
-  gfx::Size MeasureTextureSize() override;
-
-  bool primary_down_ = false;
-  bool secondary_down_ = false;
-  UiUnsupportedMode reason_ = UiUnsupportedMode::kCount;
-
-  std::unique_ptr<PromptTexture> texture_;
-  int preferred_width_ = 0;
-
-  PromptCallback result_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(Prompt);
-};
-
-}  // namespace vr
-
-#endif  // CHROME_BROWSER_VR_ELEMENTS_PROMPT_H_
diff --git a/chrome/browser/vr/elements/prompt_texture.cc b/chrome/browser/vr/elements/prompt_texture.cc
deleted file mode 100644
index 970c1cb2..0000000
--- a/chrome/browser/vr/elements/prompt_texture.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/vr/elements/prompt_texture.h"
-
-#include "base/i18n/case_conversion.h"
-#include "cc/paint/skia_paint_canvas.h"
-#include "chrome/browser/vr/elements/vector_icon.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/vector_icons/vector_icons.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/render_text.h"
-
-namespace vr {
-
-namespace {
-
-constexpr float kWidth = 0.63f;
-constexpr float kHeight = 0.218f;
-constexpr float kButtonHeight = 0.064f;
-constexpr float kCornerRadius = 0.006f;
-constexpr float kPadding = 0.028f;
-constexpr float kIconSize = 0.042f;
-constexpr float kFontSizePromptText = 0.028f;
-constexpr float kTextTopMargin = 0.007f;
-constexpr float kTextLeftMargin = 0.010f;
-constexpr float kVerticalGap = 0.056f;
-constexpr float kButtonsDistance = 0.014f;
-constexpr float kFontSizePromptButtonText = 0.024f;
-constexpr float kButtonRadius = 0.0035f;
-
-constexpr float kButtonWidth = 0.162f;
-
-constexpr char kPreferredFontNameForButtons[] = "sans-serif-medium";
-
-}  // namespace
-
-PromptTexture::PromptTexture(int content_message_id,
-                             const gfx::VectorIcon& icon,
-                             int primary_button_message_id,
-                             int secondary_button_message_id)
-    : icon_(icon),
-      primary_button_message_id_(primary_button_message_id),
-      secondary_button_message_id_(secondary_button_message_id) {
-  SetContentMessageId(content_message_id);
-}
-
-PromptTexture::~PromptTexture() = default;
-
-gfx::Size PromptTexture::GetPreferredTextureSize(int maximum_width) const {
-  return gfx::Size(maximum_width, maximum_width * kHeight / kWidth);
-}
-
-void PromptTexture::SetPrimaryButtonHovered(bool hovered) {
-  SetAndDirty(&primary_hovered_, hovered);
-}
-
-void PromptTexture::SetPrimaryButtonPressed(bool pressed) {
-  SetAndDirty(&primary_pressed_, pressed);
-}
-
-void PromptTexture::SetSecondaryButtonHovered(bool hovered) {
-  SetAndDirty(&secondary_hovered_, hovered);
-}
-
-void PromptTexture::SetSecondaryButtonPressed(bool pressed) {
-  SetAndDirty(&secondary_pressed_, pressed);
-}
-
-void PromptTexture::SetContentMessageId(int message_id) {
-  SetAndDirty(&content_message_id_, message_id);
-}
-
-bool PromptTexture::HitsSecondaryButton(const gfx::PointF& position) const {
-  return secondary_button_rect_.Contains(PercentToPixels(position));
-}
-
-bool PromptTexture::HitsPrimaryButton(const gfx::PointF& position) const {
-  return primary_button_rect_.Contains(PercentToPixels(position));
-}
-
-void PromptTexture::SetPrimaryButtonColors(const ButtonColors& colors) {
-  SetAndDirty(&primary_button_colors_, colors);
-}
-
-void PromptTexture::SetSecondaryButtonColors(const ButtonColors& colors) {
-  SetAndDirty(&secondary_button_colors_, colors);
-}
-
-void PromptTexture::SetIconColor(SkColor color) {
-  SetAndDirty(&icon_color_, color);
-}
-
-float PromptTexture::ToPixels(float meters) const {
-  return meters * size_.width() / kWidth;
-}
-
-gfx::PointF PromptTexture::PercentToPixels(const gfx::PointF& percent) const {
-  return gfx::PointF(percent.x() * size_.width(), percent.y() * size_.height());
-}
-
-void PromptTexture::Draw(SkCanvas* sk_canvas, const gfx::Size& texture_size) {
-  // TODO(cjgrant): Use a fixed scalar, like text, since size should be ~DMM.
-  size_.set_width(texture_size.width());
-  size_.set_height(texture_size.height());
-
-  // background
-  cc::SkiaPaintCanvas paint_canvas(sk_canvas);
-  gfx::Canvas gfx_canvas(&paint_canvas, 1.0f);
-  gfx::Canvas* canvas = &gfx_canvas;
-
-  SkPaint back_paint;
-  back_paint.setColor(background_color());
-  sk_canvas->drawRoundRect(SkRect::MakeWH(size_.width(), size_.height()),
-                           ToPixels(kCornerRadius), ToPixels(kCornerRadius),
-                           back_paint);
-
-  // Icon
-  gfx::PointF icon_location(ToPixels(kPadding), ToPixels(kPadding));
-  VectorIcon::DrawVectorIcon(canvas, icon_, ToPixels(kIconSize), icon_location,
-                             icon_color_);
-
-  // Prompt description.
-  auto text = l10n_util::GetStringUTF16(content_message_id_);
-  gfx::FontList fonts;
-  GetDefaultFontList(ToPixels(kFontSizePromptText), text, &fonts);
-  gfx::Rect prompt_text_size(
-      ToPixels(kWidth - 2 * kPadding - kTextLeftMargin - kIconSize), 0);
-  std::vector<std::unique_ptr<gfx::RenderText>> lines =
-      PrepareDrawStringRect(text, fonts, foreground_color(), &prompt_text_size,
-                            kTextAlignmentNone, kWrappingBehaviorWrap);
-  canvas->Save();
-  canvas->Translate(
-      gfx::Vector2d(ToPixels(kTextLeftMargin + kIconSize + kPadding),
-                    ToPixels(kPadding + kTextTopMargin)));
-  for (auto& render_text : lines)
-    render_text->Draw(canvas);
-  canvas->Restore();
-
-  // Buttons
-  SkPaint paint;
-  gfx::Rect button_text_size(ToPixels(kButtonWidth), 0);
-  float radius = ToPixels(kButtonRadius);
-
-  // Secondary button area.
-  // TODO(https://crbug.com/787654): Uppercasing should be conditional.
-  text = base::i18n::ToUpper(
-      l10n_util::GetStringUTF16(secondary_button_message_id_));
-  GetFontList(kPreferredFontNameForButtons, ToPixels(kFontSizePromptButtonText),
-              text, &fonts);
-  lines = PrepareDrawStringRect(
-      text, fonts, secondary_button_colors_.foreground, &button_text_size,
-      kTextAlignmentCenter, kWrappingBehaviorWrap);
-  secondary_button_rect_.SetRect(
-      ToPixels(kWidth - kPadding - kButtonsDistance - 2 * kButtonWidth),
-      ToPixels(kPadding + kIconSize + kVerticalGap), ToPixels(kButtonWidth),
-      ToPixels(kButtonHeight));
-  paint.setColor(secondary_button_colors_.GetBackgroundColor(
-      secondary_hovered_, secondary_pressed_));
-  canvas->Save();
-  canvas->Translate(
-      gfx::Vector2d(secondary_button_rect_.x(), secondary_button_rect_.y()));
-  sk_canvas->drawRoundRect(
-      SkRect::MakeXYWH(0, 0, ToPixels(kButtonWidth), ToPixels(kButtonHeight)),
-      radius, radius, paint);
-  canvas->Translate(gfx::Vector2d(
-      0, ToPixels(kButtonHeight) / 2 - button_text_size.height() / 2));
-  for (auto& render_text : lines)
-    render_text->Draw(canvas);
-  canvas->Restore();
-
-  // Primary button area.
-  // TODO(https://crbug.com/787654): Uppercasing should be conditional.
-  text = base::i18n::ToUpper(
-      l10n_util::GetStringUTF16(primary_button_message_id_));
-  GetFontList(kPreferredFontNameForButtons, ToPixels(kFontSizePromptButtonText),
-              text, &fonts);
-  button_text_size.set_size(gfx::Size(ToPixels(kButtonWidth), 0));
-  lines = PrepareDrawStringRect(text, fonts, primary_button_colors_.foreground,
-                                &button_text_size, kTextAlignmentCenter,
-                                kWrappingBehaviorWrap);
-  primary_button_rect_.SetRect(ToPixels(kWidth - kPadding - kButtonWidth),
-                               ToPixels(kPadding + kIconSize + kVerticalGap),
-                               ToPixels(kButtonWidth), ToPixels(kButtonHeight));
-  paint.setColor(primary_button_colors_.GetBackgroundColor(primary_hovered_,
-                                                           primary_pressed_));
-  canvas->Save();
-  canvas->Translate(
-      gfx::Vector2d(primary_button_rect_.x(), primary_button_rect_.y()));
-  sk_canvas->drawRoundRect(
-      SkRect::MakeXYWH(0, 0, ToPixels(kButtonWidth), ToPixels(kButtonHeight)),
-      radius, radius, paint);
-  canvas->Translate(gfx::Vector2d(
-      0, ToPixels(kButtonHeight) / 2 - button_text_size.height() / 2));
-  for (auto& render_text : lines)
-    render_text->Draw(canvas);
-  canvas->Restore();
-}
-
-}  // namespace vr
diff --git a/chrome/browser/vr/elements/prompt_texture.h b/chrome/browser/vr/elements/prompt_texture.h
deleted file mode 100644
index 0f35346..0000000
--- a/chrome/browser/vr/elements/prompt_texture.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_VR_ELEMENTS_PROMPT_TEXTURE_H_
-#define CHROME_BROWSER_VR_ELEMENTS_PROMPT_TEXTURE_H_
-
-#include "base/macros.h"
-#include "chrome/browser/vr/elements/ui_texture.h"
-#include "chrome/browser/vr/model/color_scheme.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/vector_icon_types.h"
-
-namespace vr {
-
-class PromptTexture : public UiTexture {
- public:
-  PromptTexture(int content_message_id,
-                const gfx::VectorIcon& icon,
-                int primary_button_message_id,
-                int secondary_button_message_id);
-  ~PromptTexture() override;
-  gfx::Size GetPreferredTextureSize(int width) const;
-
-  void SetPrimaryButtonHovered(bool hovered);
-  void SetPrimaryButtonPressed(bool pressed);
-  void SetSecondaryButtonHovered(bool hovered);
-  void SetSecondaryButtonPressed(bool pressed);
-  virtual void SetContentMessageId(int message_id);
-
-  virtual bool HitsSecondaryButton(const gfx::PointF& position) const;
-  virtual bool HitsPrimaryButton(const gfx::PointF& position) const;
-
-  void SetPrimaryButtonColors(const ButtonColors& colors);
-  void SetSecondaryButtonColors(const ButtonColors& colors);
-  void SetIconColor(SkColor color);
-
- private:
-  float ToPixels(float meters) const;
-  gfx::PointF PercentToPixels(const gfx::PointF& percent) const;
-  void Draw(SkCanvas* sk_canvas, const gfx::Size& texture_size) override;
-
-  gfx::RectF secondary_button_rect_;
-  gfx::RectF primary_button_rect_;
-  gfx::SizeF size_;
-
-  bool primary_hovered_ = false;
-  bool primary_pressed_ = false;
-  bool secondary_hovered_ = false;
-  bool secondary_pressed_ = false;
-
-  ButtonColors primary_button_colors_;
-  ButtonColors secondary_button_colors_;
-  SkColor icon_color_ = SK_ColorBLACK;
-  const gfx::VectorIcon& icon_;
-
-  int primary_button_message_id_ = -1;
-  int secondary_button_message_id_ = -1;
-  int content_message_id_ = -1;
-
-  DISALLOW_COPY_AND_ASSIGN(PromptTexture);
-};
-
-}  // namespace vr
-
-#endif  // CHROME_BROWSER_VR_ELEMENTS_PROMPT_TEXTURE_H_
diff --git a/chrome/browser/vr/elements/prompt_unittest.cc b/chrome/browser/vr/elements/prompt_unittest.cc
deleted file mode 100644
index 9363dc1..0000000
--- a/chrome/browser/vr/elements/prompt_unittest.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/vr/elements/prompt.h"
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "chrome/browser/vr/elements/prompt_texture.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/test/gfx_util.h"
-
-using ::testing::Return;
-
-namespace vr {
-
-namespace {
-
-class MockPromptTexture : public PromptTexture {
- public:
-  MockPromptTexture() : PromptTexture(-1, gfx::kNoneIcon, -1, -1) {}
-  ~MockPromptTexture() override {}
-
-  MOCK_CONST_METHOD1(HitsPrimaryButton, bool(const gfx::PointF&));
-  MOCK_CONST_METHOD1(HitsSecondaryButton, bool(const gfx::PointF&));
-
-  MOCK_CONST_METHOD1(GetPreferredTextureSize, gfx::Size(int));
-  MOCK_CONST_METHOD0(GetDrawnSize, gfx::SizeF());
-  MOCK_METHOD2(Draw, void(SkCanvas*, const gfx::Size&));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockPromptTexture);
-};
-
-}  // namespace
-
-class TestPrompt : public Prompt {
- public:
-  TestPrompt();
-  ~TestPrompt() override {}
-
-  bool primary_button_pressed() const { return primary_button_pressed_; }
-  bool secondary_button_pressed() const { return secondary_button_pressed_; }
-
- private:
-  void OnButtonPressed(Prompt::Button button, UiUnsupportedMode reason) {
-    switch (button) {
-      case Prompt::NONE:
-        break;
-      case Prompt::PRIMARY:
-        primary_button_pressed_ = true;
-        break;
-      case Prompt::SECONDARY:
-        secondary_button_pressed_ = true;
-        break;
-    }
-  }
-
-  bool primary_button_pressed_ = false;
-  bool secondary_button_pressed_ = false;
-};
-
-TestPrompt::TestPrompt()
-    : Prompt(512,
-             -1,
-             gfx::kNoneIcon,
-             -1,
-             -1,
-             base::BindRepeating(&TestPrompt::OnButtonPressed,
-                                 base::Unretained(this))) {}
-
-TEST(PromptTest, PrimaryButtonCallbackCalled) {
-  TestPrompt prompt;
-  auto texture = std::make_unique<MockPromptTexture>();
-  // Called twice from OnButtonDown and twice from OnButtonUp.
-  EXPECT_CALL(*texture, HitsPrimaryButton(gfx::PointF()))
-      .Times(4)
-      .WillRepeatedly(Return(true));
-  // Called once from OnButtonDown and once from OnButtonUp (via OnStatUpdated).
-  EXPECT_CALL(*texture, HitsSecondaryButton(gfx::PointF()))
-      .Times(2)
-      .WillRepeatedly(Return(false));
-  prompt.SetTextureForTesting(std::move(texture));
-
-  prompt.OnButtonDown(gfx::PointF());
-  prompt.OnButtonUp(gfx::PointF());
-
-  EXPECT_TRUE(prompt.primary_button_pressed());
-  EXPECT_FALSE(prompt.secondary_button_pressed());
-}
-
-TEST(PromptTest, SecondaryButtonCallbackCalled) {
-  TestPrompt prompt;
-  auto texture = std::make_unique<MockPromptTexture>();
-  // Called twice from OnButtonDown and once from OnButtonUp.
-  EXPECT_CALL(*texture, HitsPrimaryButton(gfx::PointF()))
-      .Times(3)
-      .WillRepeatedly(Return(false));
-  // Called twice from OnButtonDown and twice from OnButtonUp.
-  EXPECT_CALL(*texture, HitsSecondaryButton(gfx::PointF()))
-      .Times(4)
-      .WillRepeatedly(Return(true));
-  prompt.SetTextureForTesting(std::move(texture));
-
-  prompt.OnButtonDown(gfx::PointF());
-  prompt.OnButtonUp(gfx::PointF());
-
-  EXPECT_FALSE(prompt.primary_button_pressed());
-  EXPECT_TRUE(prompt.secondary_button_pressed());
-}
-
-}  // namespace vr
diff --git a/chrome/browser/vr/elements/text.cc b/chrome/browser/vr/elements/text.cc
index 8621c327..9cd75d2 100644
--- a/chrome/browser/vr/elements/text.cc
+++ b/chrome/browser/vr/elements/text.cc
@@ -4,15 +4,20 @@
 
 #include "chrome/browser/vr/elements/text.h"
 
+#include "base/i18n/char_iterator.h"
 #include "cc/paint/skia_paint_canvas.h"
 #include "chrome/browser/vr/elements/render_text_wrapper.h"
 #include "chrome/browser/vr/elements/ui_texture.h"
+#include "chrome/browser/vr/font_fallback.h"
+#include "third_party/icu/source/common/unicode/uscript.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/render_text.h"
+#include "ui/gfx/shadow_value.h"
+#include "ui/gfx/text_elider.h"
 
 namespace vr {
 
@@ -21,6 +26,7 @@
 constexpr float kCursorWidthRatio = 0.07f;
 constexpr int kTextPixelPerDmm = 1100;
 constexpr float kTextShadowScaleFactor = 1000.0f;
+constexpr char kDefaultFontFamily[] = "sans-serif";
 
 int DmmToPixel(float dmm) {
   return static_cast<int>(dmm * kTextPixelPerDmm);
@@ -34,6 +40,160 @@
   return mode == kSingleLineFixedWidth || mode == kMultiLineFixedWidth;
 }
 
+std::unique_ptr<gfx::RenderText> CreateRenderText(
+    const base::string16& text,
+    const gfx::FontList& font_list,
+    SkColor color,
+    TextAlignment text_alignment,
+    bool shadows_enabled,
+    SkColor shadow_color,
+    float shadow_size) {
+  auto render_text = gfx::RenderText::CreateHarfBuzzInstance();
+
+  // Disable the cursor to avoid reserving width for a trailing caret.
+  render_text->SetCursorEnabled(false);
+
+  // Subpixel rendering is counterproductive when drawing VR textures.
+  render_text->set_subpixel_rendering_suppressed(true);
+
+  render_text->SetText(text);
+  render_text->SetFontList(font_list);
+  render_text->SetColor(color);
+  if (shadows_enabled) {
+    render_text->set_shadows(
+        {gfx::ShadowValue({0, 0}, shadow_size, shadow_color)});
+  }
+
+  switch (text_alignment) {
+    case kTextAlignmentNone:
+      break;
+    case kTextAlignmentLeft:
+      render_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+      break;
+    case kTextAlignmentRight:
+      render_text->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+      break;
+    case kTextAlignmentCenter:
+      render_text->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+      break;
+  }
+
+  const int font_style = font_list.GetFontStyle();
+  render_text->SetStyle(gfx::ITALIC, (font_style & gfx::Font::ITALIC) != 0);
+  render_text->SetStyle(gfx::UNDERLINE,
+                        (font_style & gfx::Font::UNDERLINE) != 0);
+  render_text->SetWeight(font_list.GetFontWeight());
+
+  return render_text;
+}
+
+std::set<UChar32> CollectDifferentChars(base::string16 text) {
+  std::set<UChar32> characters;
+  for (base::i18n::UTF16CharIterator it(&text); !it.end(); it.Advance()) {
+    characters.insert(it.get());
+  }
+  return characters;
+}
+
+bool GetFontList(const std::string& preferred_font_name,
+                 int font_size,
+                 base::string16 text,
+                 gfx::FontList* font_list) {
+  gfx::Font preferred_font(preferred_font_name, font_size);
+  std::vector<gfx::Font> fonts{preferred_font};
+
+  std::set<std::string> names;
+  // TODO(acondor): Query BrowserProcess to obtain the application locale.
+  for (UChar32 c : CollectDifferentChars(text)) {
+    std::string name;
+    bool found_name = GetFallbackFontNameForChar(preferred_font, c, "", &name);
+    if (!found_name)
+      return false;
+    if (!name.empty())
+      names.insert(name);
+  }
+  for (const auto& name : names) {
+    DCHECK(!name.empty());
+    fonts.push_back(gfx::Font(name, font_size));
+  }
+  *font_list = gfx::FontList(fonts);
+  return true;
+}
+
+std::vector<std::unique_ptr<gfx::RenderText>> PrepareDrawStringRect(
+    const base::string16& text,
+    const gfx::FontList& font_list,
+    gfx::Rect* bounds,
+    const TextRenderParameters& parameters) {
+  DCHECK(bounds);
+
+  std::vector<std::unique_ptr<gfx::RenderText>> lines;
+
+  if (parameters.wrapping_behavior == kWrappingBehaviorWrap) {
+    DCHECK(!parameters.cursor_enabled);
+
+    gfx::Rect rect(*bounds);
+    std::vector<base::string16> strings;
+    gfx::ElideRectangleText(text, font_list, bounds->width(),
+                            bounds->height() ? bounds->height() : INT_MAX,
+                            gfx::WRAP_LONG_WORDS, &strings);
+
+    int height = 0;
+    int line_height = 0;
+    for (size_t i = 0; i < strings.size(); i++) {
+      std::unique_ptr<gfx::RenderText> render_text = CreateRenderText(
+          strings[i], font_list, parameters.color, parameters.text_alignment,
+          parameters.shadows_enabled, parameters.shadow_color,
+          parameters.shadow_size);
+
+      if (i == 0) {
+        // Measure line and center text vertically.
+        line_height = render_text->GetStringSize().height();
+        rect.set_height(line_height);
+        if (bounds->height()) {
+          const int text_height = strings.size() * line_height;
+          rect += gfx::Vector2d(0, (bounds->height() - text_height) / 2);
+        }
+      }
+
+      render_text->SetDisplayRect(rect);
+      height += line_height;
+      rect += gfx::Vector2d(0, line_height);
+      lines.push_back(std::move(render_text));
+    }
+
+    // Set calculated height.
+    if (bounds->height() == 0)
+      bounds->set_height(height);
+  } else {
+    std::unique_ptr<gfx::RenderText> render_text =
+        CreateRenderText(text, font_list, parameters.color,
+                         parameters.text_alignment, parameters.shadows_enabled,
+                         parameters.shadow_color, parameters.shadow_size);
+    if (bounds->width() != 0 && !parameters.cursor_enabled)
+      render_text->SetElideBehavior(gfx::TRUNCATE);
+    if (parameters.cursor_enabled) {
+      render_text->SetCursorEnabled(true);
+      render_text->SetCursorPosition(parameters.cursor_position);
+    }
+
+    if (bounds->width() == 0)
+      bounds->set_width(render_text->GetStringSize().width());
+    if (bounds->height() == 0)
+      bounds->set_height(render_text->GetStringSize().height());
+
+    render_text->SetDisplayRect(*bounds);
+    lines.push_back(std::move(render_text));
+  }
+
+  if (parameters.shadows_enabled) {
+    bounds->Inset(-parameters.shadow_size, -parameters.shadow_size);
+    bounds->Offset(parameters.shadow_size, parameters.shadow_size);
+  }
+
+  return lines;
+}
+
 }  // namespace
 
 TextFormattingAttribute::TextFormattingAttribute(SkColor color,
@@ -235,7 +395,7 @@
   texture_->SetFormatting(formatting);
 }
 
-void Text::SetAlignment(UiTexture::TextAlignment alignment) {
+void Text::SetAlignment(TextAlignment alignment) {
   texture_->SetAlignment(alignment);
 }
 
@@ -338,7 +498,7 @@
   }
 
   gfx::FontList fonts;
-  if (!GetDefaultFontList(pixel_font_height, text_, &fonts) ||
+  if (!GetFontList(kDefaultFontFamily, pixel_font_height, text_, &fonts) ||
       unsupported_code_point_for_test_) {
     if (unhandled_codepoint_callback_)
       unhandled_codepoint_callback_.Run();
@@ -355,10 +515,7 @@
   parameters.shadows_enabled = shadows_enabled_;
   parameters.shadow_size = kTextShadowScaleFactor * font_height_dmms_;
 
-  lines_ =
-      // TODO(vollick): if this subsumes all text, then we should probably move
-      // this function into this class.
-      PrepareDrawStringRect(text_, fonts, &text_bounds, parameters);
+  lines_ = PrepareDrawStringRect(text_, fonts, &text_bounds, parameters);
 
   if (cursor_enabled_) {
     DCHECK_EQ(lines_.size(), 1u);
diff --git a/chrome/browser/vr/elements/text.h b/chrome/browser/vr/elements/text.h
index c51f325..84bc3e08dd 100644
--- a/chrome/browser/vr/elements/text.h
+++ b/chrome/browser/vr/elements/text.h
@@ -69,6 +69,29 @@
 
 typedef std::vector<TextFormattingAttribute> TextFormatting;
 
+enum TextAlignment {
+  kTextAlignmentNone,
+  kTextAlignmentLeft,
+  kTextAlignmentCenter,
+  kTextAlignmentRight,
+};
+
+enum WrappingBehavior {
+  kWrappingBehaviorWrap,
+  kWrappingBehaviorNoWrap,
+};
+
+struct TextRenderParameters {
+  SkColor color = SK_ColorBLACK;
+  TextAlignment text_alignment = kTextAlignmentNone;
+  WrappingBehavior wrapping_behavior = kWrappingBehaviorNoWrap;
+  bool cursor_enabled = false;
+  int cursor_position = 0;
+  bool shadows_enabled = false;
+  SkColor shadow_color = SK_ColorBLACK;
+  float shadow_size = 10.0f;
+};
+
 class Text : public TexturedElement {
  public:
   explicit Text(float font_height_dmms);
@@ -87,7 +110,7 @@
   // Formatting must be applied only to non-wrapping text elements.
   void SetFormatting(const TextFormatting& formatting);
 
-  void SetAlignment(UiTexture::TextAlignment alignment);
+  void SetAlignment(TextAlignment alignment);
   void SetLayoutMode(TextLayoutMode mode);
 
   // This text element does not typically feature a cursor, but since the cursor
diff --git a/chrome/browser/vr/elements/text_button.cc b/chrome/browser/vr/elements/text_button.cc
new file mode 100644
index 0000000..145ee8b
--- /dev/null
+++ b/chrome/browser/vr/elements/text_button.cc
@@ -0,0 +1,53 @@
+// 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/vr/elements/text_button.h"
+
+#include "base/bind_helpers.h"
+#include "chrome/browser/vr/elements/rect.h"
+
+namespace vr {
+
+namespace {
+
+constexpr float kTextPaddingRatio = 0.7f;
+
+}  // namespace
+
+TextButton::TextButton(float text_size, AudioDelegate* audio_delegate)
+    : Button(base::DoNothing(), audio_delegate) {
+  set_hover_offset(0.0f);
+  set_bounds_contain_children(true);
+
+  // Add the text object to the button.
+  auto text = std::make_unique<Text>(text_size);
+  text->SetDrawPhase(kPhaseForeground);
+  text->SetType(kTypeButtonText);
+  text->SetLayoutMode(kSingleLine);
+  text->set_hit_testable(false);
+  text_ = text.get();
+  background()->AddChild(std::move(text));
+
+  // Configure background to size with text.
+  background()->set_bounds_contain_children(true);
+  background()->set_contributes_to_parent_bounds(true);
+  background()->set_padding(text_size * kTextPaddingRatio,
+                            text_size * kTextPaddingRatio);
+}
+
+TextButton::~TextButton() = default;
+
+void TextButton::SetText(const base::string16& text) {
+  text_->SetText(text);
+}
+
+void TextButton::OnSetColors(const ButtonColors& colors) {
+  if (!enabled()) {
+    text_->SetColor(colors.foreground_disabled);
+  } else {
+    text_->SetColor(colors.foreground);
+  }
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/elements/text_button.h b/chrome/browser/vr/elements/text_button.h
new file mode 100644
index 0000000..a353c2e3b
--- /dev/null
+++ b/chrome/browser/vr/elements/text_button.h
@@ -0,0 +1,34 @@
+// 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_VR_ELEMENTS_TEXT_BUTTON_H_
+#define CHROME_BROWSER_VR_ELEMENTS_TEXT_BUTTON_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/vr/elements/button.h"
+#include "chrome/browser/vr/elements/text.h"
+
+namespace vr {
+
+// TextButton is a Button that sizes itself to a supplied text string.
+class TextButton : public Button {
+ public:
+  TextButton(float text_height, AudioDelegate* audio_delegate);
+  ~TextButton() override;
+
+  void SetText(const base::string16& text);
+
+ private:
+  void OnSetColors(const ButtonColors& colors) override;
+
+  Text* text_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(TextButton);
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_ELEMENTS_TEXT_BUTTON_H_
diff --git a/chrome/browser/vr/elements/text_input.cc b/chrome/browser/vr/elements/text_input.cc
index af7a6bc..0a9f41b 100644
--- a/chrome/browser/vr/elements/text_input.cc
+++ b/chrome/browser/vr/elements/text_input.cc
@@ -28,7 +28,7 @@
   text->set_x_centering(LEFT);
   text->SetSize(1, 1);
   text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
-  text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  text->SetAlignment(kTextAlignmentLeft);
   hint_element_ = text.get();
   this->AddChild(std::move(text));
 
@@ -42,7 +42,7 @@
   text->set_bubble_events(true);
   text->SetSize(1, 1);
   text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
-  text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  text->SetAlignment(kTextAlignmentLeft);
   text->SetCursorEnabled(true);
   text_element_ = text.get();
   this->AddChild(std::move(text));
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc
index ee0dc9f..a680b17 100644
--- a/chrome/browser/vr/elements/ui_element_name.cc
+++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -42,7 +42,6 @@
     "kCeiling",
     "kFloor",
     "kStars",
-    "kUpdateKeyboardPrompt",
     "kUrlBarPositioner",
     "kUrlBarDmmRoot",
     "kUrlBar",
@@ -111,8 +110,6 @@
     "kExitWarningText",
     "kExitWarningBackground",
     "kExitPrompt",
-    "kAudioPermissionPrompt",
-    "kAudioPermissionPromptBackplane",
     "kWebVrUrlToastTransientParent",
     "kWebVrUrlToast",
     "kWebVrExclusiveScreenToast",
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h
index f85d765c..2e9e4af8 100644
--- a/chrome/browser/vr/elements/ui_element_name.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -41,7 +41,6 @@
   kCeiling,
   kFloor,
   kStars,
-  kUpdateKeyboardPrompt,
   kUrlBarPositioner,
   kUrlBarDmmRoot,
   kUrlBar,
@@ -110,8 +109,6 @@
   kExitWarningText,
   kExitWarningBackground,
   kExitPrompt,
-  kAudioPermissionPrompt,
-  kAudioPermissionPromptBackplane,
   kWebVrUrlToastTransientParent,
   kWebVrUrlToast,
   kWebVrExclusiveScreenToast,
diff --git a/chrome/browser/vr/elements/ui_element_type.cc b/chrome/browser/vr/elements/ui_element_type.cc
index 442ef95..69c9e7b 100644
--- a/chrome/browser/vr/elements/ui_element_type.cc
+++ b/chrome/browser/vr/elements/ui_element_type.cc
@@ -16,6 +16,7 @@
     "kTypeButtonBackground",
     "kTypeButtonForeground",
     "kTypeButtonHitTarget",
+    "kTypeButtonText",
     "kTypeHostedUiBackplane",
     "kTypeScaledDepthAdjuster",
     "kTypeOmniboxSuggestionBackground",
@@ -27,6 +28,11 @@
     "kTypeOmniboxSuggestionDescriptionText",
     "kTypePromptBackplane",
     "kTypePromptShadow",
+    "kTypePromptBackground",
+    "kTypePromptIcon",
+    "kTypePromptText",
+    "kTypePromptPrimaryButton",
+    "kTypePromptSecondaryButton",
     "kTypeSpacer",
     "kTypeTextInputHint",
     "kTypeTextInputText",
@@ -43,7 +49,7 @@
 
 static_assert(
     kNumUiElementTypes == arraysize(g_ui_element_type_strings),
-    "Mismatch between the kUiElementName enum and the corresponding array "
+    "Mismatch between the kUiElementType enum and the corresponding array "
     "of strings.");
 
 }  // namespace
diff --git a/chrome/browser/vr/elements/ui_element_type.h b/chrome/browser/vr/elements/ui_element_type.h
index 321a075..fdcc80e 100644
--- a/chrome/browser/vr/elements/ui_element_type.h
+++ b/chrome/browser/vr/elements/ui_element_type.h
@@ -16,6 +16,7 @@
   kTypeButtonBackground,
   kTypeButtonForeground,
   kTypeButtonHitTarget,
+  kTypeButtonText,
   kTypeHostedUiBackplane,
   kTypeScaledDepthAdjuster,
   kTypeOmniboxSuggestionBackground,
@@ -27,6 +28,11 @@
   kTypeOmniboxSuggestionDescriptionText,
   kTypePromptBackplane,
   kTypePromptShadow,
+  kTypePromptBackground,
+  kTypePromptIcon,
+  kTypePromptText,
+  kTypePromptPrimaryButton,
+  kTypePromptSecondaryButton,
   kTypeSpacer,
   kTypeTextInputHint,
   kTypeTextInputText,
diff --git a/chrome/browser/vr/elements/ui_texture.cc b/chrome/browser/vr/elements/ui_texture.cc
index 8be24b2..398cadd 100644
--- a/chrome/browser/vr/elements/ui_texture.cc
+++ b/chrome/browser/vr/elements/ui_texture.cc
@@ -4,42 +4,13 @@
 
 #include "chrome/browser/vr/elements/ui_texture.h"
 
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/i18n/char_iterator.h"
-#include "base/i18n/rtl.h"
-#include "base/strings/string_util.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/vr/font_fallback.h"
-#include "third_party/icu/source/common/unicode/uscript.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/render_text.h"
-#include "ui/gfx/shadow_value.h"
-#include "ui/gfx/text_elider.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace vr {
 
-namespace {
-
-constexpr char kDefaultFontFamily[] = "sans-serif";
-
-static bool force_font_fallback_failure_for_testing_ = false;
-
-std::set<UChar32> CollectDifferentChars(base::string16 text) {
-  std::set<UChar32> characters;
-  for (base::i18n::UTF16CharIterator it(&text); !it.end(); it.Advance()) {
-    characters.insert(it.get());
-  }
-  return characters;
-}
-
-}  // namespace
-
 UiTexture::UiTexture() = default;
 
 UiTexture::~UiTexture() = default;
@@ -63,184 +34,6 @@
   set_dirty();
 }
 
-std::vector<std::unique_ptr<gfx::RenderText>> UiTexture::PrepareDrawStringRect(
-    const base::string16& text,
-    const gfx::FontList& font_list,
-    SkColor color,
-    gfx::Rect* bounds,
-    UiTexture::TextAlignment text_alignment,
-    UiTexture::WrappingBehavior wrapping_behavior) {
-  TextRenderParameters parameters;
-  parameters.color = color;
-  parameters.text_alignment = text_alignment;
-  parameters.wrapping_behavior = wrapping_behavior;
-  return PrepareDrawStringRect(text, font_list, bounds, parameters);
-}
-
-std::vector<std::unique_ptr<gfx::RenderText>> UiTexture::PrepareDrawStringRect(
-    const base::string16& text,
-    const gfx::FontList& font_list,
-    gfx::Rect* bounds,
-    const TextRenderParameters& parameters) {
-  DCHECK(bounds);
-
-  std::vector<std::unique_ptr<gfx::RenderText>> lines;
-
-  if (parameters.wrapping_behavior == kWrappingBehaviorWrap) {
-    DCHECK(!parameters.cursor_enabled);
-
-    gfx::Rect rect(*bounds);
-    std::vector<base::string16> strings;
-    gfx::ElideRectangleText(text, font_list, bounds->width(),
-                            bounds->height() ? bounds->height() : INT_MAX,
-                            gfx::WRAP_LONG_WORDS, &strings);
-
-    int height = 0;
-    int line_height = 0;
-    for (size_t i = 0; i < strings.size(); i++) {
-      std::unique_ptr<gfx::RenderText> render_text = CreateConfiguredRenderText(
-          strings[i], font_list, parameters.color, parameters.text_alignment,
-          parameters.shadows_enabled, parameters.shadow_color,
-          parameters.shadow_size);
-
-      if (i == 0) {
-        // Measure line and center text vertically.
-        line_height = render_text->GetStringSize().height();
-        rect.set_height(line_height);
-        if (bounds->height()) {
-          const int text_height = strings.size() * line_height;
-          rect += gfx::Vector2d(0, (bounds->height() - text_height) / 2);
-        }
-      }
-
-      render_text->SetDisplayRect(rect);
-      height += line_height;
-      rect += gfx::Vector2d(0, line_height);
-      lines.push_back(std::move(render_text));
-    }
-
-    // Set calculated height.
-    if (bounds->height() == 0)
-      bounds->set_height(height);
-  } else {
-    std::unique_ptr<gfx::RenderText> render_text = CreateConfiguredRenderText(
-        text, font_list, parameters.color, parameters.text_alignment,
-        parameters.shadows_enabled, parameters.shadow_color,
-        parameters.shadow_size);
-    if (bounds->width() != 0 && !parameters.cursor_enabled)
-      render_text->SetElideBehavior(gfx::TRUNCATE);
-    if (parameters.cursor_enabled) {
-      render_text->SetCursorEnabled(true);
-      render_text->SetCursorPosition(parameters.cursor_position);
-    }
-
-    if (bounds->width() == 0)
-      bounds->set_width(render_text->GetStringSize().width());
-    if (bounds->height() == 0)
-      bounds->set_height(render_text->GetStringSize().height());
-
-    render_text->SetDisplayRect(*bounds);
-    lines.push_back(std::move(render_text));
-  }
-
-  if (parameters.shadows_enabled) {
-    bounds->Inset(-parameters.shadow_size, -parameters.shadow_size);
-    bounds->Offset(parameters.shadow_size, parameters.shadow_size);
-  }
-
-  return lines;
-}
-
-std::unique_ptr<gfx::RenderText> UiTexture::CreateRenderText() {
-  auto render_text = gfx::RenderText::CreateHarfBuzzInstance();
-
-  // Disable the cursor to avoid reserving width for a trailing caret.
-  render_text->SetCursorEnabled(false);
-
-  // Subpixel rendering is counterproductive when drawing VR textures.
-  render_text->set_subpixel_rendering_suppressed(true);
-
-  return render_text;
-}
-
-std::unique_ptr<gfx::RenderText> UiTexture::CreateConfiguredRenderText(
-    const base::string16& text,
-    const gfx::FontList& font_list,
-    SkColor color,
-    UiTexture::TextAlignment text_alignment,
-    bool shadows_enabled,
-    SkColor shadow_color,
-    float shadow_size) {
-  std::unique_ptr<gfx::RenderText> render_text(CreateRenderText());
-  render_text->SetText(text);
-  render_text->SetFontList(font_list);
-  render_text->SetColor(color);
-  if (shadows_enabled) {
-    render_text->set_shadows(
-        {gfx::ShadowValue({0, 0}, shadow_size, shadow_color)});
-  }
-
-  switch (text_alignment) {
-    case UiTexture::kTextAlignmentNone:
-      break;
-    case UiTexture::kTextAlignmentLeft:
-      render_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-      break;
-    case UiTexture::kTextAlignmentRight:
-      render_text->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-      break;
-    case UiTexture::kTextAlignmentCenter:
-      render_text->SetHorizontalAlignment(gfx::ALIGN_CENTER);
-      break;
-  }
-
-  const int font_style = font_list.GetFontStyle();
-  render_text->SetStyle(gfx::ITALIC, (font_style & gfx::Font::ITALIC) != 0);
-  render_text->SetStyle(gfx::UNDERLINE,
-                        (font_style & gfx::Font::UNDERLINE) != 0);
-  render_text->SetWeight(font_list.GetFontWeight());
-
-  return render_text;
-}
-
-bool UiTexture::IsRTL() {
-  return base::i18n::IsRTL();
-}
-
-bool UiTexture::GetFontList(const std::string& preferred_font_name,
-                            int font_size,
-                            base::string16 text,
-                            gfx::FontList* font_list) {
-  if (force_font_fallback_failure_for_testing_)
-    return false;
-
-  gfx::Font preferred_font(preferred_font_name, font_size);
-  std::vector<gfx::Font> fonts{preferred_font};
-
-  std::set<std::string> names;
-  // TODO(acondor): Query BrowserProcess to obtain the application locale.
-  for (UChar32 c : CollectDifferentChars(text)) {
-    std::string name;
-    bool found_name = GetFallbackFontNameForChar(preferred_font, c, "", &name);
-    if (!found_name)
-      return false;
-    if (!name.empty())
-      names.insert(name);
-  }
-  for (const auto& name : names) {
-    DCHECK(!name.empty());
-    fonts.push_back(gfx::Font(name, font_size));
-  }
-  *font_list = gfx::FontList(fonts);
-  return true;
-}
-
-bool UiTexture::GetDefaultFontList(int font_size,
-                                   base::string16 text,
-                                   gfx::FontList* font_list) {
-  return GetFontList(kDefaultFontFamily, font_size, text, font_list);
-}
-
 SkColor UiTexture::foreground_color() const {
   DCHECK(foreground_color_);
   return foreground_color_.value();
@@ -265,8 +58,4 @@
   set_dirty();
 }
 
-void UiTexture::SetForceFontFallbackFailureForTesting(bool force) {
-  force_font_fallback_failure_for_testing_ = force;
-}
-
 }  // namespace vr
diff --git a/chrome/browser/vr/elements/ui_texture.h b/chrome/browser/vr/elements/ui_texture.h
index 87c617f..eee4a1a 100644
--- a/chrome/browser/vr/elements/ui_texture.h
+++ b/chrome/browser/vr/elements/ui_texture.h
@@ -10,7 +10,6 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
-#include "base/strings/string16.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -20,9 +19,7 @@
 
 namespace gfx {
 
-class FontList;
 class PointF;
-class RenderText;
 
 }  // namespace gfx
 
@@ -53,45 +50,6 @@
   void SetForegroundColor(SkColor color);
   void SetBackgroundColor(SkColor color);
 
-  // This function sets |font_list| to a list of available fonts for |text|. If
-  // no font supports |text|, it returns false and leave |font_list| untouched.
-  static bool GetDefaultFontList(int font_size,
-                                 base::string16 text,
-                                 gfx::FontList* font_list);
-
-  // This function sets |font_list| to a list of available fonts for |text|. If
-  // the font with |preferred_font_name| is available and supports |text|,
-  // |font_list| will be configured to use the preferred font instead of default
-  // font. If no font supports |text|, it returns false and leave |font_list|
-  // untouched.
-  static bool GetFontList(const std::string& preferred_font_name,
-                          int font_size,
-                          base::string16 text,
-                          gfx::FontList* font_list);
-
-  enum TextAlignment {
-    kTextAlignmentNone,
-    kTextAlignmentLeft,
-    kTextAlignmentCenter,
-    kTextAlignmentRight,
-  };
-
-  enum WrappingBehavior {
-    kWrappingBehaviorWrap,
-    kWrappingBehaviorNoWrap,
-  };
-
-  struct TextRenderParameters {
-    SkColor color = SK_ColorBLACK;
-    TextAlignment text_alignment = kTextAlignmentNone;
-    WrappingBehavior wrapping_behavior = kWrappingBehaviorNoWrap;
-    bool cursor_enabled = false;
-    int cursor_position = 0;
-    bool shadows_enabled = false;
-    SkColor shadow_color = SK_ColorBLACK;
-    float shadow_size = 10.0f;
-  };
-
  protected:
   template <typename T>
   void SetAndDirty(T* target, const T& value) {
@@ -100,42 +58,6 @@
     *target = value;
   }
 
-  // Prepares a set of RenderText objects with the given parameters.
-  // Attempts to fit the text within the provided size. |flags| specifies how
-  // the text should be rendered. If multiline is requested and provided height
-  // is 0, it will be set to the minimum needed to fit the whole text. If
-  // multiline is not requested and provided width is 0, it will be set to the
-  // minimum needed to fit the whole text.
-  static std::vector<std::unique_ptr<gfx::RenderText>> PrepareDrawStringRect(
-      const base::string16& text,
-      const gfx::FontList& font_list,
-      gfx::Rect* bounds,
-      const TextRenderParameters& parameters);
-
-  // Deprecated legacy text prep function. UI elements that use this routine
-  // should migrate to use Text elements, rather than drawing text directly.
-  static std::vector<std::unique_ptr<gfx::RenderText>> PrepareDrawStringRect(
-      const base::string16& text,
-      const gfx::FontList& font_list,
-      SkColor color,
-      gfx::Rect* bounds,
-      TextAlignment text_alignment,
-      WrappingBehavior wrapping_behavior);
-
-  static std::unique_ptr<gfx::RenderText> CreateRenderText();
-
-  static std::unique_ptr<gfx::RenderText> CreateConfiguredRenderText(
-      const base::string16& text,
-      const gfx::FontList& font_list,
-      SkColor color,
-      TextAlignment text_alignment,
-      bool shadows_enabled,
-      SkColor shadow_color,
-      float shadow_size);
-
-  static bool IsRTL();
-  static void SetForceFontFallbackFailureForTesting(bool force);
-
   void set_dirty() {
     measured_ = false;
     dirty_ = true;
diff --git a/chrome/browser/vr/model/modal_prompt_type.cc b/chrome/browser/vr/model/modal_prompt_type.cc
index dc0387c..33d3853 100644
--- a/chrome/browser/vr/model/modal_prompt_type.cc
+++ b/chrome/browser/vr/model/modal_prompt_type.cc
@@ -22,6 +22,8 @@
       return UiUnsupportedMode::kNeedsKeyboardUpdate;
     case kModalPromptTypeNone:
       return UiUnsupportedMode::kCount;
+    case kNumModalPromptTypes:
+      break;
   }
   NOTREACHED();
   return UiUnsupportedMode::kCount;
diff --git a/chrome/browser/vr/model/modal_prompt_type.h b/chrome/browser/vr/model/modal_prompt_type.h
index f16f04f..0556791 100644
--- a/chrome/browser/vr/model/modal_prompt_type.h
+++ b/chrome/browser/vr/model/modal_prompt_type.h
@@ -16,6 +16,8 @@
   kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission,
   kModalPromptTypeGenericUnsupportedFeature,
   kModalPromptTypeUpdateKeyboard,
+
+  kNumModalPromptTypes
 };
 
 UiUnsupportedMode GetReasonForPrompt(ModalPromptType prompt);
diff --git a/chrome/browser/vr/test/mock_browser_ui_interface.h b/chrome/browser/vr/test/mock_browser_ui_interface.h
index 161cac2..d427c70d 100644
--- a/chrome/browser/vr/test/mock_browser_ui_interface.h
+++ b/chrome/browser/vr/test/mock_browser_ui_interface.h
@@ -46,6 +46,7 @@
                void(int id, bool incognito, const base::string16& title));
   MOCK_METHOD2(RemoveTab, void(int id, bool incognito));
   MOCK_METHOD0(RemoveAllTabs, void());
+  MOCK_METHOD2(OnTabSelected, void(int id, bool incognito));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockBrowserUiInterface);
diff --git a/chrome/browser/vr/test/mock_ui_browser_interface.h b/chrome/browser/vr/test/mock_ui_browser_interface.h
index 23b2d23..28e58fb 100644
--- a/chrome/browser/vr/test/mock_ui_browser_interface.h
+++ b/chrome/browser/vr/test/mock_ui_browser_interface.h
@@ -23,6 +23,7 @@
   MOCK_METHOD0(NavigateForward, void());
   MOCK_METHOD0(ReloadTab, void());
   MOCK_METHOD1(OpenNewTab, void(bool));
+  MOCK_METHOD2(SelectTab, void(int id, bool incognito));
   MOCK_METHOD0(OpenBookmarks, void());
   MOCK_METHOD0(OpenRecentTabs, void());
   MOCK_METHOD0(OpenHistory, void());
@@ -41,8 +42,8 @@
   MOCK_METHOD1(SetVoiceSearchActive, void(bool active));
   MOCK_METHOD1(StartAutocomplete, void(const AutocompleteRequest& request));
   MOCK_METHOD0(StopAutocomplete, void());
-  MOCK_METHOD0(ShowPageInfo, void());
   MOCK_METHOD0(LoadAssets, void());
+  MOCK_METHOD0(ShowPageInfo, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockUiBrowserInterface);
diff --git a/chrome/browser/vr/test/ui_test.cc b/chrome/browser/vr/test/ui_test.cc
index cf1e997f..e2c08be 100644
--- a/chrome/browser/vr/test/ui_test.cc
+++ b/chrome/browser/vr/test/ui_test.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/vr/test/constants.h"
 #include "chrome/browser/vr/test/fake_ui_element_renderer.h"
 #include "chrome/browser/vr/ui.h"
+#include "chrome/browser/vr/ui_renderer.h"
 #include "chrome/browser/vr/ui_scene.h"
 #include "chrome/browser/vr/ui_scene_creator.h"
 #include "third_party/blink/public/platform/web_gesture_event.h"
@@ -252,4 +253,32 @@
   *background_color = color;
 }
 
+void UiTest::ClickElement(UiElement* element) {
+  // Synthesize a controller vector targeting the element.
+  gfx::Point3F target;
+  element->ComputeTargetWorldSpaceTransform().TransformPoint(&target);
+  gfx::Point3F origin;
+  gfx::Vector3dF direction(target - origin);
+  direction.GetNormalized(&direction);
+
+  RenderInfo render_info;
+  ReticleModel reticle_model;
+  GestureList gesture_list;
+  ControllerModel controller_model;
+  controller_model.laser_direction = direction;
+  controller_model.laser_origin = origin;
+
+  controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN;
+  ui_->input_manager()->HandleInput(current_time_, render_info,
+                                    controller_model, &reticle_model,
+                                    &gesture_list);
+  OnBeginFrame();
+
+  controller_model.touchpad_button_state = UiInputManager::ButtonState::UP;
+  ui_->input_manager()->HandleInput(current_time_, render_info,
+                                    controller_model, &reticle_model,
+                                    &gesture_list);
+  OnBeginFrame();
+}
+
 }  // namespace vr
diff --git a/chrome/browser/vr/test/ui_test.h b/chrome/browser/vr/test/ui_test.h
index 2d488e291..1e348eb6 100644
--- a/chrome/browser/vr/test/ui_test.h
+++ b/chrome/browser/vr/test/ui_test.h
@@ -106,6 +106,11 @@
 
   void GetBackgroundColor(SkColor* background_color) const;
 
+  // Synthesize a controller orientation that intersects the element, and cycle
+  // the controller button.  This offers a reasonably correct means of testing
+  // clicks on elements, that's true to hit testability, visbility, etc.
+  void ClickElement(UiElement* element);
+
   std::unique_ptr<Ui> ui_;
   std::unique_ptr<MockUiBrowserInterface> browser_;
   MockContentInputDelegate* content_input_delegate_ = nullptr;
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index 94e85e2..fdf2a6ef 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -59,6 +59,10 @@
 constexpr gfx::Vector3dF kLaserLocalOffset = {0.f, -0.0075f, -0.05f};
 constexpr float kControllerScaleFactor = 1.5f;
 constexpr float kTouchpadPositionDelta = 0.05f;
+const float kVerticalScrollScaleFactor =
+    -8.0f / ui::MouseWheelEvent::kWheelDelta;
+const float kHorizontalScrollScaleFactor =
+    60.0f / ui::MouseWheelEvent::kWheelDelta;
 constexpr gfx::PointF kInitialTouchPosition = {0.5f, 0.5f};
 
 void RotateToward(const gfx::Vector3dF& fwd, gfx::Transform* transform) {
@@ -75,15 +79,20 @@
       out_image->get());
 }
 
-GestureList CreateScrollGestureList(blink::WebInputEvent::Type type,
-                                    const gfx::Vector2dF& delta) {
-  auto event = std::make_unique<blink::WebGestureEvent>();
-  event->SetType(type);
-  event->data.scroll_update.delta_x = delta.x();
-  event->data.scroll_update.delta_y = delta.y();
-  GestureList gesture_list;
-  gesture_list.push_back(std::move(event));
-  return gesture_list;
+GestureList CreateScrollGestureEventList(blink::WebGestureEvent::Type type) {
+  auto gesture = std::make_unique<blink::WebGestureEvent>();
+  gesture->SetType(type);
+  GestureList list;
+  list.push_back(std::move(gesture));
+  return list;
+}
+
+GestureList CreateScrollGestureEventList(blink::WebGestureEvent::Type type,
+                                         const gfx::Vector2dF& delta) {
+  auto list = CreateScrollGestureEventList(type);
+  list.front()->data.scroll_update.delta_x = delta.x();
+  list.front()->data.scroll_update.delta_y = delta.y();
+  return list;
 }
 
 }  // namespace
@@ -250,32 +259,16 @@
       case ui::DomCode::US_T:
         touching_touchpad_ = !touching_touchpad_;
         break;
-      case ui::DomCode::US_Q:
+      case ui::DomCode::US_Q: {
+        auto mode = model_->active_modal_prompt_type;
         model_->active_modal_prompt_type =
-            kModalPromptTypeGenericUnsupportedFeature;
+            static_cast<ModalPromptType>((mode + 1) % kNumModalPromptTypes);
         model_->push_mode(kModeModalPrompt);
         break;
+      }
       case ui::DomCode::US_L:
         model_->standalone_vr_device = !model_->standalone_vr_device;
         break;
-      case ui::DomCode::ARROW_RIGHT:
-        gesture_lists_.push(CreateScrollGestureList(
-            blink::WebInputEvent::kGestureScrollBegin, gfx::Vector2dF(0, 0)));
-        gesture_lists_.push(
-            CreateScrollGestureList(blink::WebInputEvent::kGestureScrollUpdate,
-                                    gfx::Vector2dF(-100, 0)));
-        gesture_lists_.push(CreateScrollGestureList(
-            blink::WebInputEvent::kGestureScrollEnd, gfx::Vector2dF(0, 0)));
-        break;
-      case ui::DomCode::ARROW_LEFT:
-        gesture_lists_.push(CreateScrollGestureList(
-            blink::WebInputEvent::kGestureScrollBegin, gfx::Vector2dF(0, 0)));
-        gesture_lists_.push(
-            CreateScrollGestureList(blink::WebInputEvent::kGestureScrollUpdate,
-                                    gfx::Vector2dF(100, 0)));
-        gesture_lists_.push(CreateScrollGestureList(
-            blink::WebInputEvent::kGestureScrollEnd, gfx::Vector2dF(0, 0)));
-        break;
       default:
         break;
     }
@@ -286,13 +279,29 @@
     int direction =
         base::ClampToRange(event->AsMouseWheelEvent()->y_offset(), -1, 1);
     if (event->IsControlDown()) {
+      view_scale_factor_ *= (1 + direction * kViewScaleAdjustmentFactor);
+      view_scale_factor_ = base::ClampToRange(
+          view_scale_factor_, kMinViewScaleFactor, kMaxViewScaleFactor);
+    } else if (model_->reposition_window_enabled()) {
       touchpad_touch_position_.set_y(base::ClampToRange(
           touchpad_touch_position_.y() + kTouchpadPositionDelta * direction,
           0.0f, 1.0f));
     } else {
-      view_scale_factor_ *= (1 + direction * kViewScaleAdjustmentFactor);
-      view_scale_factor_ = base::ClampToRange(
-          view_scale_factor_, kMinViewScaleFactor, kMaxViewScaleFactor);
+      gesture_lists_.push(CreateScrollGestureEventList(
+          blink::WebGestureEvent::kGestureScrollBegin));
+
+      auto offset = gfx::Vector2dF(event->AsMouseWheelEvent()->offset());
+      if (event->IsShiftDown()) {
+        offset.Scale(kHorizontalScrollScaleFactor);
+        offset = gfx::Vector2dF(offset.y(), offset.x());
+      } else {
+        offset.Scale(kVerticalScrollScaleFactor);
+      }
+      gesture_lists_.push(CreateScrollGestureEventList(
+          blink::WebGestureEvent::kGestureScrollUpdate, offset));
+
+      gesture_lists_.push(CreateScrollGestureEventList(
+          blink::WebGestureEvent::kGestureScrollEnd));
     }
     return;
   }
@@ -396,7 +405,6 @@
   // Hit testing is done in terms of this synthesized controller model.
   if (gesture_lists_.empty()) {
     gesture_lists_.push(GestureList());
-    gesture_lists_.back().push_back(std::make_unique<blink::WebGestureEvent>());
   }
   ReticleModel reticle_model;
   ui_->input_manager()->HandleInput(current_time, render_info, controller_model,
@@ -571,6 +579,10 @@
   ui_->AddOrUpdateTab(tab_id_++, incognito, base::UTF8ToUTF16("test"));
 }
 
+void VrTestContext::SelectTab(int id, bool incognito) {
+  ui_->OnTabSelected(id, incognito);
+}
+
 void VrTestContext::OpenBookmarks() {}
 void VrTestContext::OpenRecentTabs() {}
 void VrTestContext::OpenHistory() {}
diff --git a/chrome/browser/vr/testapp/vr_test_context.h b/chrome/browser/vr/testapp/vr_test_context.h
index 691423d..a2dca7a 100644
--- a/chrome/browser/vr/testapp/vr_test_context.h
+++ b/chrome/browser/vr/testapp/vr_test_context.h
@@ -48,6 +48,7 @@
   void NavigateForward() override;
   void ReloadTab() override;
   void OpenNewTab(bool incognito) override;
+  void SelectTab(int id, bool incognito) override;
   void OpenBookmarks() override;
   void OpenRecentTabs() override;
   void OpenHistory() override;
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index 8922d0a..0238224 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/vr/content_input_delegate.h"
 #include "chrome/browser/vr/cpu_surface_provider.h"
 #include "chrome/browser/vr/elements/content_element.h"
-#include "chrome/browser/vr/elements/prompt.h"
 #include "chrome/browser/vr/elements/text_input.h"
 #include "chrome/browser/vr/ganesh_surface_provider.h"
 #include "chrome/browser/vr/keyboard_delegate.h"
@@ -268,6 +267,10 @@
   model_->incognito_tabs.clear();
 }
 
+void Ui::OnTabSelected(int id, bool incognito) {
+  model_->pop_mode(kModeTabsView);
+}
+
 bool Ui::CanSendWebVrVSync() {
   return model_->web_vr_enabled() &&
          !model_->web_vr.awaiting_min_splash_screen_duration() &&
@@ -542,14 +545,14 @@
 
 void Ui::AcceptDoffPromptForTesting() {
   DCHECK(model_->active_modal_prompt_type != kModalPromptTypeNone);
-  if (model_->active_modal_prompt_type ==
-      kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission) {
-    static_cast<Prompt*>(scene_->GetUiElementByName(kAudioPermissionPrompt))
-        ->ClickPrimaryButtonForTesting();
-  } else {
-    static_cast<Prompt*>(scene_->GetUiElementByName(kExitPrompt))
-        ->ClickSecondaryButtonForTesting();
-  }
+  auto* prompt = scene_->GetUiElementByName(kExitPrompt);
+  DCHECK(prompt);
+  auto* button = prompt->GetDescendantByType(kTypePromptSecondaryButton);
+  DCHECK(button);
+  button->OnHoverEnter({0.5f, 0.5f});
+  button->OnButtonDown({0.5f, 0.5f});
+  button->OnButtonUp({0.5f, 0.5f});
+  button->OnHoverLeave();
 }
 
 void Ui::PerformUiActionForTesting(UiTestInput test_input) {
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h
index d12453f..0c6cdfc 100644
--- a/chrome/browser/vr/ui.h
+++ b/chrome/browser/vr/ui.h
@@ -116,6 +116,7 @@
                       const base::string16& title) override;
   void RemoveTab(int id, bool incognito) override;
   void RemoveAllTabs() override;
+  void OnTabSelected(int id, bool incognito) override;
 
   // TODO(ymalik): We expose this to stop sending VSync to the WebVR page until
   // the splash screen has been visible for its minimum duration. The visibility
diff --git a/chrome/browser/vr/ui_browser_interface.h b/chrome/browser/vr/ui_browser_interface.h
index 6c2a3c61..c30db2b 100644
--- a/chrome/browser/vr/ui_browser_interface.h
+++ b/chrome/browser/vr/ui_browser_interface.h
@@ -33,6 +33,7 @@
   virtual void NavigateForward() = 0;
   virtual void ReloadTab() = 0;
   virtual void OpenNewTab(bool incognito) = 0;
+  virtual void SelectTab(int id, bool incognito) = 0;
   virtual void OpenBookmarks() = 0;
   virtual void OpenRecentTabs() = 0;
   virtual void OpenHistory() = 0;
diff --git a/chrome/browser/vr/ui_input_manager_unittest.cc b/chrome/browser/vr/ui_input_manager_unittest.cc
index 091aa6e..5cc0b89 100644
--- a/chrome/browser/vr/ui_input_manager_unittest.cc
+++ b/chrome/browser/vr/ui_input_manager_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/time/time.h"
 #include "cc/test/geometry_test_utils.h"
 #include "chrome/browser/vr/content_input_delegate.h"
-#include "chrome/browser/vr/elements/prompt.h"
+#include "chrome/browser/vr/elements/invisible_hit_target.h"
 #include "chrome/browser/vr/elements/rect.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "chrome/browser/vr/model/model.h"
@@ -449,8 +449,8 @@
   // Even if the reticle is over the URL bar, the backplane should be in front
   // and should be hit.
   ASSERT_NE(0, reticle_model.target_element_id);
-  auto* backplane = scene_->GetUiElementByName(kAudioPermissionPromptBackplane);
-  EXPECT_EQ(backplane->type(), kTypePromptBackplane);
+  auto* prompt = scene_->GetUiElementByName(kExitPrompt);
+  auto* backplane = prompt->GetDescendantByType(kTypePromptBackplane);
   EXPECT_EQ(backplane->id(), reticle_model.target_element_id);
 }
 
diff --git a/chrome/browser/vr/ui_scene_constants.h b/chrome/browser/vr/ui_scene_constants.h
index 183c345..90b5da3345 100644
--- a/chrome/browser/vr/ui_scene_constants.h
+++ b/chrome/browser/vr/ui_scene_constants.h
@@ -140,7 +140,6 @@
 static constexpr float kTimeoutMessageIconWidthDMM = 0.056f;
 static constexpr float kTimeoutMessageIconHeightDMM = 0.056f;
 static constexpr float kTimeoutMessageTextFontHeightDMM = 0.022f;
-static constexpr float kTimeoutMessageTextHeightDMM = 0.056f;
 static constexpr float kTimeoutMessageTextWidthDMM = 0.4f;
 
 static constexpr float kTimeoutButtonDepthOffset = -0.1f;
@@ -148,7 +147,6 @@
 static constexpr float kWebVrTimeoutMessageButtonDiameterDMM = 0.096f;
 
 static constexpr float kTimeoutButtonTextWidthDMM = 0.058f;
-static constexpr float kTimeoutButtonTextHeightDMM = 0.024f;
 static constexpr float kTimeoutButtonTextVerticalOffsetDMM = 0.024f;
 
 static constexpr float kHostedUiHeightRatio = 0.6f;
@@ -210,15 +208,6 @@
 static constexpr float kKeyboardVerticalOffsetDMM = -0.45f;
 static constexpr float kKeyboardWebInputOffset = 1.2f;
 
-static constexpr float kSnackbarDistance = 1.5f;
-static constexpr float kSnackbarAngle = -gfx::DegToRad(34.0f);
-static constexpr float kSnackbarPaddingDMM = 0.032f;
-static constexpr float kSnackbarIconWidthDMM = 0.034f;
-static constexpr float kSnackbarFontHeightDMM = 0.024f;
-static constexpr float kSnackbarHeightDMM = 0.08f;
-static constexpr float kSnackbarMoveInAngle = -base::kPiFloat / 10;
-static constexpr int kSnackbarTransitionDurationMs = 300;
-
 static constexpr float kControllerLabelSpacerSize = 0.025f;
 static constexpr float kControllerLabelLayoutMargin = -0.005f;
 static constexpr float kControllerLabelCalloutWidth = 0.02f;
@@ -262,11 +251,19 @@
 static constexpr int kWebVrPermissionOffsetMs = 250;
 static constexpr int kWebVrPermissionAnimationDurationMs = 750;
 
-static constexpr float kPromptWidthDMM = 0.63f;
-static constexpr float kPromptHeightDMM = 0.218f;
 static constexpr float kPromptVerticalOffsetDMM = -0.1f;
 static constexpr float kPromptShadowOffsetDMM = 0.1f;
 static constexpr float kPromptDistance = 2.4f;
+static constexpr float kPromptPadding = 0.028f;
+static constexpr float kPromptCornerRadius = 0.006f;
+static constexpr float kPromptTextWidth = 0.522f;
+static constexpr float kPromptFontSize = 0.028f;
+static constexpr float kPromptIconSize = 0.042f;
+static constexpr float kPromptButtonCornerRadius = 0.0035f;
+static constexpr float kPromptIconTextGap = 0.010f;
+static constexpr float kPromptMessageButtonGap = 0.056f;
+static constexpr float kPromptButtonTextSize = 0.024f;
+static constexpr float kPromptButtonGap = 0.014f;
 
 static constexpr float kRepositionCursorBackgroundSize = 1.85f;
 static constexpr float kRepositionCursorSize = 1.5f;
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index d02ebd33..f780a83 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/vr/elements/oval.h"
 #include "chrome/browser/vr/elements/paged_grid_layout.h"
 #include "chrome/browser/vr/elements/paged_scroll_view.h"
-#include "chrome/browser/vr/elements/prompt.h"
 #include "chrome/browser/vr/elements/rect.h"
 #include "chrome/browser/vr/elements/repositioner.h"
 #include "chrome/browser/vr/elements/resizer.h"
@@ -46,6 +45,7 @@
 #include "chrome/browser/vr/elements/scaled_depth_adjuster.h"
 #include "chrome/browser/vr/elements/spinner.h"
 #include "chrome/browser/vr/elements/text.h"
+#include "chrome/browser/vr/elements/text_button.h"
 #include "chrome/browser/vr/elements/text_input.h"
 #include "chrome/browser/vr/elements/throbber.h"
 #include "chrome/browser/vr/elements/transient_element.h"
@@ -166,7 +166,7 @@
   content_text->SetType(kTypeOmniboxSuggestionContentText);
   content_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
   content_text->SetFieldWidth(kSuggestionTextFieldWidthDMM);
-  content_text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  content_text->SetAlignment(kTextAlignmentLeft);
   Text* p_content_text = content_text.get();
 
   auto description_text =
@@ -175,7 +175,7 @@
   description_text->SetType(kTypeOmniboxSuggestionDescriptionText);
   description_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
   description_text->SetFieldWidth(kSuggestionTextFieldWidthDMM);
-  description_text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  description_text->SetAlignment(kTextAlignmentLeft);
   Text* p_description_text = description_text.get();
 
   auto text_layout = std::make_unique<LinearLayout>(LinearLayout::kDown);
@@ -280,12 +280,15 @@
                      Model* model,
                      bool incognito,
                      PagedGridLayout* tabs_view,
+                     AudioDelegate* audio_delegate,
+                     UiBrowserInterface* browser,
                      TabBinding* element_binding) {
-  auto item = Create<Rect>(kNone, kPhaseForeground);
-  item->SetColor(ColorScheme::GetColorScheme(incognito
-                                                 ? ColorScheme::kModeIncognito
-                                                 : ColorScheme::kModeNormal)
-                     .tab_item_background);
+  auto item = Create<Button>(kNone, kPhaseForeground, base::RepeatingClosure(),
+                             audio_delegate);
+  item->SetButtonColors(
+      ColorScheme::GetColorScheme(incognito ? ColorScheme::kModeIncognito
+                                            : ColorScheme::kModeNormal)
+          .disc_button_colors);
   item->SetSize(kTabItemWidthDMM, kTabItemHeightDMM);
   item->SetTransitionedProperties({OPACITY});
   item->set_corner_radius(kTabItemCornerRadiusDMM);
@@ -296,20 +299,39 @@
           },
           base::Unretained(tabs_view), base::Unretained(item.get())),
       VR_BIND_LAMBDA(
-          [](UiElement* item, const PagedGridLayout::PageState& page_state) {
+          [](Button* item, const PagedGridLayout::PageState& page_state) {
             switch (page_state) {
               case PagedGridLayout::kActive:
                 item->SetOpacity(kTabsViewActivePageOpacity);
+                item->SetEnabled(true);
                 break;
               case PagedGridLayout::kInactive:
                 item->SetOpacity(kTabsViewInactivePageOpacity);
+                item->SetEnabled(false);
                 break;
               default:
                 item->SetOpacity(kTabsViewHiddenPageOpacity);
+                item->SetEnabled(false);
                 break;
             }
           },
           base::Unretained(item.get()))));
+  element_binding->bindings().push_back(std::make_unique<Binding<int>>(
+      VR_BIND_LAMBDA(
+          [](TabBinding* element_binding) {
+            return element_binding->model()->id;
+          },
+          base::Unretained(element_binding)),
+      VR_BIND_LAMBDA(
+          [](Button* item, UiBrowserInterface* browser, bool incognito,
+             const int& id) {
+            item->set_click_handler(base::BindRepeating(
+                [](UiBrowserInterface* browser, int id, bool incognito) {
+                  browser->SelectTab(id, incognito);
+                },
+                base::Unretained(browser), id, incognito));
+          },
+          base::Unretained(item.get()), base::Unretained(browser), incognito)));
 
   // TODO(crbug.com/838937): This is just a placeholder text. Replace with
   // proper tab item.
@@ -324,7 +346,7 @@
                                                  ? ColorScheme::kModeIncognito
                                                  : ColorScheme::kModeNormal)
                      .tab_item_text);
-  item->AddChild(std::move(text));
+  item->background()->AddChild(std::move(text));
 
   scene->AddUiElement(tabs_view, std::move(item));
 }
@@ -352,58 +374,109 @@
   return spacer;
 }
 
-std::pair<std::unique_ptr<UiElement>, Prompt*> CreatePrompt(
-    UiElementName name,
-    UiElementName backplane_name,
-    Model* model,
-    int content_message_id,
-    const gfx::VectorIcon& icon,
-    int primary_button_message_id,
-    int secondary_button_message_id,
-    const Prompt::PromptCallback& result_callback) {
-  auto prompt = Create<Prompt>(name, kPhaseForeground, 1024, content_message_id,
-                               icon, primary_button_message_id,
-                               secondary_button_message_id, result_callback);
-  auto* prompt_ptr = prompt.get();
-  prompt->SetDrawPhase(kPhaseForeground);
-  prompt->SetTranslate(0, 0, kPromptShadowOffsetDMM);
-  prompt->SetSize(kPromptWidthDMM, kPromptHeightDMM);
-  prompt->set_hit_testable(true);
-  VR_BIND_BUTTON_COLORS(model, prompt.get(),
+std::unique_ptr<UiElement> CreatePrompt(Model* model) {
+  auto primary_button = Create<TextButton>(kNone, kPhaseForeground,
+                                           kPromptButtonTextSize, nullptr);
+  primary_button->SetType(kTypePromptPrimaryButton);
+  primary_button->set_corner_radius(kPromptButtonCornerRadius);
+  VR_BIND_BUTTON_COLORS(model, primary_button.get(),
                         &ColorScheme::modal_prompt_primary_button_colors,
-                        &Prompt::SetPrimaryButtonColors);
-  VR_BIND_BUTTON_COLORS(model, prompt.get(),
-                        &ColorScheme::modal_prompt_secondary_button_colors,
-                        &Prompt::SetSecondaryButtonColors);
-  VR_BIND_COLOR(model, prompt.get(), &ColorScheme::modal_prompt_icon_foreground,
-                &Prompt::SetIconColor);
-  VR_BIND_COLOR(model, prompt.get(), &ColorScheme::modal_prompt_background,
-                &TexturedElement::SetBackgroundColor);
-  VR_BIND_COLOR(model, prompt.get(), &ColorScheme::modal_prompt_foreground,
-                &TexturedElement::SetForegroundColor);
+                        &Button::SetButtonColors);
 
-  // Place an invisible but hittable plane behind the exit prompt, to keep the
-  // reticle roughly planar with the content if near content.
-  auto backplane = Create<InvisibleHitTarget>(backplane_name, kPhaseForeground);
-  backplane->SetType(kTypePromptBackplane);
-  backplane->SetSize(kBackplaneSize, kBackplaneSize);
-  backplane->SetTranslate(0, kPromptVerticalOffsetDMM, 0);
-  backplane->SetTransitionedProperties({OPACITY});
-  EventHandlers event_handlers;
-  event_handlers.button_up = base::BindRepeating(
-      [](Prompt* prompt) { prompt->Cancel(); }, base::Unretained(prompt.get()));
-  backplane->set_event_handlers(event_handlers);
+  auto secondary_button = Create<TextButton>(kNone, kPhaseForeground,
+                                             kPromptButtonTextSize, nullptr);
+  secondary_button->SetType(kTypePromptSecondaryButton);
+  secondary_button->set_corner_radius(kPromptButtonCornerRadius);
+  VR_BIND_BUTTON_COLORS(model, secondary_button.get(),
+                        &ColorScheme::modal_prompt_secondary_button_colors,
+                        &Button::SetButtonColors);
+
+  auto button_layout =
+      Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kLeft);
+  button_layout->AddChild(std::move(primary_button));
+  button_layout->AddChild(std::move(secondary_button));
+  button_layout->set_margin(kPromptButtonGap);
+  button_layout->set_x_anchoring(RIGHT);
+
+  auto icon = Create<VectorIcon>(kNone, kPhaseForeground, 100);
+  icon->SetType(kTypePromptIcon);
+  icon->SetSize(kPromptIconSize, kPromptIconSize);
+  icon->set_y_anchoring(TOP);
+  VR_BIND_COLOR(model, icon.get(), &ColorScheme::modal_prompt_icon_foreground,
+                &VectorIcon::SetColor);
+
+  auto text = Create<Text>(kNone, kPhaseForeground, kPromptFontSize);
+  text->SetType(kTypePromptText);
+  text->SetLayoutMode(kMultiLineFixedWidth);
+  text->SetAlignment(kTextAlignmentLeft);
+  text->SetFieldWidth(kPromptTextWidth);
+  VR_BIND_COLOR(model, text.get(), &ColorScheme::modal_prompt_foreground,
+                &Text::SetColor);
+
+  // This spacer's padding ensures that the top line of text is aligned with the
+  // icon even in the multi-line case.
+  auto text_spacer = CreateSpacer(0, 0);
+  text_spacer->set_bounds_contain_children(true);
+  text_spacer->set_padding(0, (kPromptIconSize - kPromptFontSize) / 2, 0, 0);
+  text_spacer->set_y_anchoring(TOP);
+  text_spacer->AddChild(std::move(text));
+
+  auto message_layout =
+      Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
+  message_layout->set_margin(kPromptIconTextGap);
+  message_layout->AddChild(std::move(icon));
+  message_layout->AddChild(std::move(text_spacer));
+
+  auto prompt_layout =
+      Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kDown);
+  prompt_layout->set_margin(kPromptMessageButtonGap);
+  prompt_layout->AddChild(std::move(message_layout));
+  prompt_layout->AddChild(std::move(button_layout));
+
+  auto background = Create<Rect>(kNone, kPhaseForeground);
+  background->SetType(kTypePromptBackground);
+  background->set_bounds_contain_children(true);
+  background->set_hit_testable(true);
+  background->set_padding(kPromptPadding, kPromptPadding);
+  background->SetTranslate(0, 0, kPromptShadowOffsetDMM);
+  background->set_corner_radius(kPromptCornerRadius);
+  background->AddChild(std::move(prompt_layout));
+  VR_BIND_COLOR(model, background.get(), &ColorScheme::modal_prompt_background,
+                &Rect::SetColor);
 
   auto shadow = Create<Shadow>(kNone, kPhaseForeground);
   shadow->SetType(kTypePromptShadow);
-  shadow->AddChild(std::move(prompt));
+  shadow->AddChild(std::move(background));
+
+  // Place an invisible but hittable plane behind the exit prompt, to keep the
+  // reticle roughly planar with the content if near content.
+  auto backplane = Create<InvisibleHitTarget>(kNone, kPhaseForeground);
+  backplane->SetType(kTypePromptBackplane);
+  backplane->SetTranslate(0, kPromptVerticalOffsetDMM, 0);
+  backplane->SetSize(kBackplaneSize, kBackplaneSize);
+  backplane->SetTransitionedProperties({OPACITY});
   backplane->AddChild(std::move(shadow));
 
   auto scaler = Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kPromptDistance);
   scaler->SetType(kTypeScaledDepthAdjuster);
   scaler->AddChild(std::move(backplane));
   scaler->set_contributes_to_parent_bounds(false);
-  return {std::move(scaler), prompt_ptr};
+  return scaler;
+}
+
+base::RepeatingCallback<void()> CreatePromptCallback(
+    UiUnsupportedMode mode,
+    ExitVrPromptChoice choice,
+    Model* model,
+    UiBrowserInterface* browser) {
+  return base::BindRepeating(
+      [](UiUnsupportedMode mode, ExitVrPromptChoice choice, Model* m,
+         UiBrowserInterface* b) {
+        b->OnExitVrPromptResult(choice, mode);
+        m->active_modal_prompt_type = kModalPromptTypeNone;
+        m->pop_mode(kModeModalPrompt);
+      },
+      mode, choice, base::Unretained(model), base::Unretained(browser));
 }
 
 std::unique_ptr<UiElement> CreateControllerLabel(UiElementName name,
@@ -442,7 +515,7 @@
   label->SetText(text);
   label->SetColor(model->color_scheme().controller_label_callout);
   label->SetVisible(true);
-  label->SetAlignment(UiTexture::kTextAlignmentRight);
+  label->SetAlignment(kTextAlignmentRight);
   label->SetLayoutMode(kSingleLine);
   label->SetRotate(1, 0, 0, -base::kPiFloat / 2);
   label->SetShadowsEnabled(true);
@@ -609,7 +682,7 @@
     auto text_element = Create<Text>(kNone, kPhaseOverlayForeground,
                                      kWebVrPermissionFontHeight);
     text_element->SetLayoutMode(kMultiLineFixedWidth);
-    text_element->SetAlignment(UiTexture::kTextAlignmentLeft);
+    text_element->SetAlignment(kTextAlignmentLeft);
     text_element->SetColor(SK_ColorWHITE);
     text_element->SetFieldWidth(kWebVrPermissionTextWidth);
     if (spec.signal)
@@ -807,6 +880,8 @@
 
 std::unique_ptr<UiElement> CreateTabsView(Model* model,
                                           UiScene* scene,
+                                          AudioDelegate* audio_delegate,
+                                          UiBrowserInterface* browser,
                                           bool incognito) {
   auto tabs_scroll_view = Create<PagedScrollView>(
       kNone, kPhaseNone,
@@ -829,7 +904,8 @@
 
   TabSetBinding::ModelAddedCallback added_callback = base::BindRepeating(
       &OnTabModelAdded, base::Unretained(scene), base::Unretained(model),
-      incognito, base::Unretained(tabs_layout.get()));
+      incognito, base::Unretained(tabs_layout.get()),
+      base::Unretained(audio_delegate), base::Unretained(browser));
   TabSetBinding::ModelRemovedCallback removed_callback =
       base::BindRepeating(&OnTabModelRemoved, base::Unretained(scene));
   tabs_layout->AddBinding(std::make_unique<TabSetBinding>(
@@ -1425,7 +1501,7 @@
       l10n_util::GetStringUTF16(IDS_VR_WEB_VR_TIMEOUT_MESSAGE));
   timeout_text->SetColor(
       model_->color_scheme().web_vr_timeout_message_foreground);
-  timeout_text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  timeout_text->SetAlignment(kTextAlignmentLeft);
   timeout_text->SetFieldWidth(kTimeoutMessageTextWidthDMM);
   timeout_text->set_hit_testable(true);
 
@@ -1617,7 +1693,7 @@
   speech_result->SetDrawPhase(kPhaseForeground);
   speech_result->SetTranslate(0.f, kSpeechRecognitionResultTextYOffset, 0.f);
   speech_result->SetFieldWidth(kVoiceSearchRecognitionResultTextWidth);
-  speech_result->SetAlignment(UiTexture::kTextAlignmentCenter);
+  speech_result->SetAlignment(kTextAlignmentCenter);
   VR_BIND_COLOR(model_, speech_result.get(), &ColorScheme::prompt_foreground,
                 &Text::SetColor);
   speech_result->AddBinding(VR_BIND_FUNC(base::string16, Model, model_,
@@ -2076,7 +2152,7 @@
   hint_text->SetFieldWidth(kUrlBarOriginRegionWidthDMM -
                            kUrlBarOriginContentOffsetDMM);
   hint_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
-  hint_text->SetAlignment(UiTexture::kTextAlignmentLeft);
+  hint_text->SetAlignment(kTextAlignmentLeft);
   hint_text->SetText(l10n_util::GetStringUTF16(IDS_SEARCH_OR_TYPE_WEB_ADDRESS));
   VR_BIND_COLOR(model_, hint_text.get(), &ColorScheme::url_bar_hint_text,
                 &Text::SetColor);
@@ -2268,7 +2344,7 @@
     text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
     text->SetFieldWidth(kOverflowMenuMinimumWidth -
                         2 * kOverflowMenuItemXPadding);
-    text->SetAlignment(UiTexture::kTextAlignmentLeft);
+    text->SetAlignment(kTextAlignmentLeft);
     text->AddBinding(VR_BIND_FUNC(
         SkColor, Model, model_, model->color_scheme().url_bar_button.foreground,
         Text, text.get(), SetColor));
@@ -2722,102 +2798,103 @@
 }
 
 void UiSceneCreator::CreatePrompts() {
-  auto prompt_callback = base::BindRepeating(
-      [](Model* model, UiBrowserInterface* browser, Prompt::Button button,
-         UiUnsupportedMode mode) {
-        ExitVrPromptChoice choice = CHOICE_NONE;
-        switch (button) {
-          case Prompt::NONE:
-            choice = CHOICE_NONE;
-            break;
-          case Prompt::PRIMARY:
-            choice = CHOICE_EXIT;
-            break;
-          case Prompt::SECONDARY:
-            choice = CHOICE_STAY;
-            break;
-        }
-        browser->OnExitVrPromptResult(choice, mode);
-        model->active_modal_prompt_type = kModalPromptTypeNone;
-        model->pop_mode(kModeModalPrompt);
-      },
-      base::Unretained(model_), base::Unretained(browser_));
-  // Create audio permission prompt.
-  auto prompt = CreatePrompt(
-      kAudioPermissionPrompt, kAudioPermissionPromptBackplane, model_,
-      IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_DESCRIPTION, vector_icons::kMicIcon,
-      IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON,
-      IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON, prompt_callback);
-  prompt.second->set_reason(
-      UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission);
-  VR_BIND_VISIBILITY(
-      prompt.first,
-      model->active_modal_prompt_type ==
-          kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission);
-  scene_->AddUiElement(k2dBrowsingRepositioner, std::move(prompt.first));
-
-  // Create keyboard update prompt. Note that we re-use the same button texts as
-  // the audio permission prompt.
-  prompt = CreatePrompt(
-      kUpdateKeyboardPrompt, kNone, model_, IDS_VR_UPDATE_KEYBOARD_PROMPT,
-      vector_icons::kInfoOutlineIcon,
-      IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON,
-      IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON, prompt_callback);
-  prompt.second->set_reason(UiUnsupportedMode::kNeedsKeyboardUpdate);
-  VR_BIND_VISIBILITY(prompt.first, model->active_modal_prompt_type ==
-                                       kModalPromptTypeUpdateKeyboard);
-  scene_->AddUiElement(k2dBrowsingRepositioner, std::move(prompt.first));
-
-  // Create generic unsupported UI exit prompt.
-  prompt_callback = base::BindRepeating(
-      [](Model* model, UiBrowserInterface* browser, Prompt::Button button,
-         UiUnsupportedMode mode) {
-        ExitVrPromptChoice choice = CHOICE_NONE;
-        switch (button) {
-          case Prompt::NONE:
-            choice = CHOICE_NONE;
-            break;
-          case Prompt::PRIMARY:
-            choice = CHOICE_STAY;
-            break;
-          case Prompt::SECONDARY:
-            choice = CHOICE_EXIT;
-            break;
-        }
-        browser->OnExitVrPromptResult(choice, mode);
-        model->active_modal_prompt_type = kModalPromptTypeNone;
-        model->pop_mode(kModeModalPrompt);
-      },
-      base::Unretained(model_), base::Unretained(browser_));
-  prompt = CreatePrompt(
-      kExitPrompt, kNone, model_, IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION,
-      vector_icons::kInfoOutlineIcon, IDS_OK,
-      IDS_VR_SHELL_EXIT_PROMPT_EXIT_VR_BUTTON, prompt_callback);
-  VR_BIND_VISIBILITY(
-      prompt.first,
-      model->active_modal_prompt_type != kModalPromptTypeNone &&
-          model->active_modal_prompt_type !=
-              kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission &&
-          model->active_modal_prompt_type != kModalPromptTypeUpdateKeyboard);
-  prompt.second->AddBinding(std::make_unique<Binding<ModalPromptType>>(
+  auto prompt = CreatePrompt(model_);
+  prompt->SetName(kExitPrompt);
+  VR_BIND_VISIBILITY(prompt,
+                     model->active_modal_prompt_type != kModalPromptTypeNone);
+  prompt->AddBinding(std::make_unique<Binding<ModalPromptType>>(
       VR_BIND_LAMBDA([](Model* m) { return m->active_modal_prompt_type; },
                      base::Unretained(model_)),
       VR_BIND_LAMBDA(
-          [](Prompt* e, const ModalPromptType& p) {
-            e->set_reason(GetReasonForPrompt(p));
-            switch (p) {
-              case kModalPromptTypeExitVRForSiteInfo:
-                e->SetContentMessageId(
-                    IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION_SITE_INFO);
+          [](UiElement* e, Model* model, UiBrowserInterface* browser,
+             const ModalPromptType& type) {
+            if (type == kModalPromptTypeNone)
+              return;
+
+            int message_id = 0;
+            const gfx::VectorIcon* icon = nullptr;
+            int primary_button_text_id = 0;
+            int secondary_button_text_id = 0;
+            ExitVrPromptChoice primary_choice = CHOICE_NONE;
+            ExitVrPromptChoice secondary_choice = CHOICE_NONE;
+            UiUnsupportedMode reason = GetReasonForPrompt(type);
+
+            switch (type) {
+              case kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission:
+                message_id = IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_DESCRIPTION;
+                icon = &vector_icons::kMicIcon;
+                primary_button_text_id =
+                    IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON;
+                secondary_button_text_id =
+                    IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON;
+                primary_choice = CHOICE_EXIT;
+                secondary_choice = CHOICE_STAY;
                 break;
-              case kModalPromptTypeGenericUnsupportedFeature:  // Fall through.
-              default:
-                e->SetContentMessageId(IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION);
+              case kModalPromptTypeUpdateKeyboard:
+                message_id = IDS_VR_UPDATE_KEYBOARD_PROMPT;
+                icon = &vector_icons::kInfoOutlineIcon;
+                primary_button_text_id =
+                    IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON;
+                secondary_button_text_id =
+                    IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON;
+                primary_choice = CHOICE_EXIT;
+                secondary_choice = CHOICE_STAY;
+                break;
+              case kModalPromptTypeExitVRForSiteInfo:
+                message_id = IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION_SITE_INFO;
+                icon = &vector_icons::kInfoOutlineIcon;
+                primary_button_text_id = IDS_OK;
+                secondary_button_text_id =
+                    IDS_VR_SHELL_EXIT_PROMPT_EXIT_VR_BUTTON;
+                primary_choice = CHOICE_STAY;
+                secondary_choice = CHOICE_EXIT;
+                break;
+              case kModalPromptTypeExitVRForConnectionInfo:
+              case kModalPromptTypeGenericUnsupportedFeature:
+                message_id = IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION;
+                icon = &vector_icons::kInfoOutlineIcon;
+                primary_button_text_id = IDS_OK;
+                secondary_button_text_id =
+                    IDS_VR_SHELL_EXIT_PROMPT_EXIT_VR_BUTTON;
+                primary_choice = CHOICE_STAY;
+                secondary_choice = CHOICE_EXIT;
+                break;
+              case kNumModalPromptTypes:
+              case kModalPromptTypeNone:
+                NOTREACHED();
                 break;
             }
+            Text* text_element =
+                static_cast<Text*>(e->GetDescendantByType(kTypePromptText));
+            text_element->SetText(l10n_util::GetStringUTF16(message_id));
+            VectorIcon* icon_element = static_cast<VectorIcon*>(
+                e->GetDescendantByType(kTypePromptIcon));
+            icon_element->SetIcon(icon);
+            TextButton* primary_button = static_cast<TextButton*>(
+                e->GetDescendantByType(kTypePromptPrimaryButton));
+            // TODO(crbug.com/787654): Uppercasing should be conditional.
+            primary_button->SetText(base::i18n::ToUpper(
+                l10n_util::GetStringUTF16(primary_button_text_id)));
+            primary_button->set_click_handler(
+                CreatePromptCallback(reason, primary_choice, model, browser));
+            TextButton* secondary_button = static_cast<TextButton*>(
+                e->GetDescendantByType(kTypePromptSecondaryButton));
+            // TODO(crbug.com/787654): Uppercasing should be conditional.
+            secondary_button->SetText(base::i18n::ToUpper(
+                l10n_util::GetStringUTF16(secondary_button_text_id)));
+            secondary_button->set_click_handler(
+                CreatePromptCallback(reason, secondary_choice, model, browser));
+            InvisibleHitTarget* backplane = static_cast<InvisibleHitTarget*>(
+                e->GetDescendantByType(kTypePromptBackplane));
+            EventHandlers event_handlers;
+            event_handlers.button_up =
+                CreatePromptCallback(reason, CHOICE_NONE, model, browser);
+            backplane->set_event_handlers(event_handlers);
+
           },
-          base::Unretained(prompt.second))));
-  scene_->AddUiElement(k2dBrowsingRepositioner, std::move(prompt.first));
+          base::Unretained(prompt.get()), base::Unretained(model_),
+          base::Unretained(browser_))));
+  scene_->AddUiElement(k2dBrowsingRepositioner, std::move(prompt));
 }
 
 void UiSceneCreator::CreateWebVrOverlayElements() {
@@ -3045,11 +3122,13 @@
   VR_BIND_VISIBILITY(tabs_view_root,
                      model->get_last_opaque_mode() == kModeTabsView);
 
-  auto regular_tabs_view = CreateTabsView(model_, scene_, false);
+  auto regular_tabs_view =
+      CreateTabsView(model_, scene_, audio_delegate_, browser_, false);
   VR_BIND_VISIBILITY(regular_tabs_view, !model->incognito);
   tabs_view_root->AddChild(std::move(regular_tabs_view));
 
-  auto incognito_tabs_view = CreateTabsView(model_, scene_, true);
+  auto incognito_tabs_view =
+      CreateTabsView(model_, scene_, audio_delegate_, browser_, true);
   VR_BIND_VISIBILITY(incognito_tabs_view, model->incognito);
   tabs_view_root->AddChild(std::move(incognito_tabs_view));
 
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index 72ee306..a22e6940 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/vr/elements/content_element.h"
 #include "chrome/browser/vr/elements/disc_button.h"
 #include "chrome/browser/vr/elements/indicator_spec.h"
-#include "chrome/browser/vr/elements/prompt.h"
 #include "chrome/browser/vr/elements/rect.h"
 #include "chrome/browser/vr/elements/repositioner.h"
 #include "chrome/browser/vr/elements/ui_element.h"
@@ -545,13 +544,12 @@
   auto* omnibox = scene_->GetUiElementByName(kUrlBarOriginRegion);
   EXPECT_CALL(*browser_,
               OnUnsupportedMode(UiUnsupportedMode::kNeedsKeyboardUpdate));
-  omnibox->OnHoverEnter({0.5, 0.5});
-  omnibox->OnButtonUp({0.5, 0.5});
+  ClickElement(omnibox);
   ui_->ShowExitVrPrompt(UiUnsupportedMode::kNeedsKeyboardUpdate);
   OnBeginFrame();
   EXPECT_EQ(model_->active_modal_prompt_type,
             ModalPromptType::kModalPromptTypeUpdateKeyboard);
-  EXPECT_TRUE(scene_->GetUiElementByName(kUpdateKeyboardPrompt)->IsVisible());
+  EXPECT_TRUE(scene_->GetUiElementByName(kExitPrompt)->IsVisible());
 }
 
 TEST_F(UiTest, WebInputEditingTriggersUnsupportedMode) {
@@ -568,7 +566,7 @@
   OnBeginFrame();
   EXPECT_EQ(model_->active_modal_prompt_type,
             ModalPromptType::kModalPromptTypeUpdateKeyboard);
-  EXPECT_TRUE(scene_->GetUiElementByName(kUpdateKeyboardPrompt)->IsVisible());
+  EXPECT_TRUE(scene_->GetUiElementByName(kExitPrompt)->IsVisible());
 }
 
 TEST_F(UiTest, ExitWebInputEditingOnAppButtonClick) {
@@ -602,18 +600,37 @@
 
   // Initial state.
   VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing);
-  model_->active_modal_prompt_type = kModalPromptTypeExitVRForSiteInfo;
+  ui_->ShowExitVrPrompt(UiUnsupportedMode::kUnhandledPageInfo);
   OnBeginFrame();
 
   // Click on 'OK' should trigger UI browser interface and close prompt.
   EXPECT_CALL(*browser_,
               OnExitVrPromptResult(ExitVrPromptChoice::CHOICE_STAY,
                                    UiUnsupportedMode::kUnhandledPageInfo));
-  static_cast<Prompt*>(scene_->GetUiElementByName(kExitPrompt))
-      ->ClickPrimaryButtonForTesting();
+  auto* prompt = scene_->GetUiElementByName(kExitPrompt);
+  auto* button = prompt->GetDescendantByType(kTypePromptPrimaryButton);
+  ClickElement(button);
   VerifyOnlyElementsVisible("Prompt cleared", kElementsVisibleInBrowsing);
 }
 
+TEST_F(UiTest, ClickOnPromptBackgroundDoesNothing) {
+  CreateScene(kNotInCct, kNotInWebVr);
+
+  ui_->ShowExitVrPrompt(UiUnsupportedMode::kUnhandledPageInfo);
+  OnBeginFrame();
+
+  EXPECT_CALL(*browser_, OnExitVrPromptResult(testing::_, testing::_)).Times(0);
+  auto* prompt = scene_->GetUiElementByName(kExitPrompt);
+  UiElement* target;
+  // Target the inert icon and text to reliably miss the buttons.
+  target = prompt->GetDescendantByType(kTypePromptIcon);
+  ClickElement(target);
+  target = prompt->GetDescendantByType(kTypePromptText);
+  ClickElement(target);
+
+  EXPECT_EQ(model_->get_mode(), kModeModalPrompt);
+}
+
 TEST_F(UiTest, SecondaryButtonClickTriggersOnExitPrompt) {
   CreateScene(kNotInCct, kNotInWebVr);
 
@@ -627,8 +644,9 @@
               OnExitVrPromptResult(ExitVrPromptChoice::CHOICE_EXIT,
                                    UiUnsupportedMode::kUnhandledPageInfo));
 
-  static_cast<Prompt*>(scene_->GetUiElementByName(kExitPrompt))
-      ->ClickSecondaryButtonForTesting();
+  auto* prompt = scene_->GetUiElementByName(kExitPrompt);
+  auto* button = prompt->GetDescendantByType(kTypePromptSecondaryButton);
+  ClickElement(button);
   VerifyOnlyElementsVisible("Prompt cleared", kElementsVisibleInBrowsing);
 }
 
@@ -962,7 +980,9 @@
       base::string16(), base::string16(), ACMatchClassifications(),
       ACMatchClassifications(), AutocompleteMatch::Type::VOICE_SUGGEST, gurl,
       base::string16(), base::string16()));
-  OnBeginFrame();
+
+  // Let the omnibox fade in.
+  RunForMs(100);
 
   UiElement* suggestions = scene_->GetUiElementByName(kOmniboxSuggestions);
   ASSERT_NE(suggestions, nullptr);
@@ -971,9 +991,7 @@
   EXPECT_CALL(*browser_,
               Navigate(gurl, NavigationMethod::kOmniboxSuggestionSelected))
       .Times(1);
-  suggestion->OnHoverEnter({0, 0});
-  suggestion->OnButtonDown({0, 0});
-  suggestion->OnButtonUp({0, 0});
+  ClickElement(suggestion);
 }
 
 TEST_F(UiTest, ControllerQuiescence) {
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 713a57ad..34bb65c 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -243,9 +243,6 @@
 // disable that check. This switch is used during automated testing.
 const char kDisablePromptOnRepost[]         = "disable-prompt-on-repost";
 
-// Enable background mode for the Push API.
-const char kDisablePushApiBackgroundMode[] = "disable-push-api-background-mode";
-
 // Disables showing the search geolocation disclosure UI. Used for perf testing.
 const char kDisableSearchGeolocationDisclosure[] =
     "disable-search-geolocation-disclosure";
@@ -338,9 +335,6 @@
 const char kEnablePrintPreviewRegisterPromos[] =
     "enable-print-preview-register-promos";
 
-// Enable background mode for the Push API.
-const char kEnablePushApiBackgroundMode[] = "enable-push-api-background-mode";
-
 // Enables user control over muting tab audio from the tab strip.
 const char kEnableTabAudioMuting[]  = "enable-tab-audio-muting";
 
@@ -651,10 +645,6 @@
 // Used for testing.
 const char kSupervisedUserId[]              = "managed-user-id";
 
-// Used to authenticate requests to the Sync service for supervised users.
-// Setting this switch also causes Sync to be set up for a supervised user.
-const char kSupervisedUserSyncToken[]       = "managed-user-sync-token";
-
 // Frequency in Milliseconds for system log uploads. Should only be used for
 // testing purposes.
 const char kSystemLogUploadFrequency[] = "system-log-upload-frequency";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index d822440..6aae74d2 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -83,7 +83,6 @@
 extern const char kDisablePopupBlocking[];
 extern const char kDisablePrintPreview[];
 extern const char kDisablePromptOnRepost[];
-extern const char kDisablePushApiBackgroundMode[];
 extern const char kDisableSearchGeolocationDisclosure[];
 extern const char kDisableZeroBrowsersOpenForTests[];
 extern const char kDiskCacheDir[];
@@ -108,7 +107,6 @@
 extern const char kEnablePotentiallyAnnoyingSecurityFeatures[];
 extern const char kEnablePowerOverlay[];
 extern const char kEnablePrintPreviewRegisterPromos[];
-extern const char kEnablePushApiBackgroundMode[];
 extern const char kEnableTabAudioMuting[];
 extern const char kEnableUiDevTools[];
 extern const char kExtensionContentVerification[];
@@ -188,7 +186,6 @@
 extern const char kStartMaximized[];
 extern const char kStartStackProfiler[];
 extern const char kSupervisedUserId[];
-extern const char kSupervisedUserSyncToken[];
 extern const char kSystemLogUploadFrequency[];
 extern const char kTaskManagerShowExtraRenderers[];
 extern const char kTestName[];
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 90ba05d4..cec0caa8 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -35,7 +35,8 @@
        "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/717501
        "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/717501
        "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/717501
-       "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/717501
+       "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/717501
+       "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "accessibilityPrivate.onTwoFingerTouchStop": {
@@ -52,7 +53,8 @@
        "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/717501
        "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/717501
        "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/717501
-       "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/717501
+       "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/717501
+       "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "action": {
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 88afe22..1c97ddd 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -148,7 +148,8 @@
       "96FF2FFA5C9173C76D47184B3E86D267B37781DE",  // http://crbug.com/642141
       "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F",  // http://crbug.com/642141
       "9834387FDA1F66A1B5CA06CB442137B556F12F2A",  // http://crbug.com/772346
-      "930F7D9989A5FBCDCCD7D85BB5C3B7006C24D91D"   // http://crbug.com/782139
+      "930F7D9989A5FBCDCCD7D85BB5C3B7006C24D91D",  // http://crbug.com/782139
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "clipboardRead": {
@@ -730,7 +731,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // https://crbug.com/803362
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // https://crbug.com/803362
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // https://crbug.com/803362
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // https://crbug.com/803362
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // https://crbug.com/803362
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // https://crbug.com/839189
     ]
   }],
   "signedInDevices": {
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index d7b7507..a827b7c 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -1072,6 +1072,13 @@
   [nocompile]
   static void getRecentFiles(SourceRestriction restriction,
                              GetRecentFilesCallback callback);
+
+  // Returns true if crostini is enabled.
+  // |callback|
+  static void isCrostiniEnabled(BooleanCallback callback);
+
+  // Starts and mounts crostini container.
+  static void mountCrostiniContainer();
 };
 
 interface Events {
diff --git a/chrome/common/trace_event_args_whitelist.cc b/chrome/common/trace_event_args_whitelist.cc
index d7eb322..d0744bf 100644
--- a/chrome/common/trace_event_args_whitelist.cc
+++ b/chrome/common/trace_event_args_whitelist.cc
@@ -36,6 +36,7 @@
     {nullptr, nullptr, nullptr}};
 
 const char* kMetadataWhitelist[] = {
+  "chrome-library-name",
   "clock-domain",
   "config",
   "cpu-*",
diff --git a/chrome/notification_helper/notification_activator.cc b/chrome/notification_helper/notification_activator.cc
index 19d6f1c..746681b 100644
--- a/chrome/notification_helper/notification_activator.cc
+++ b/chrome/notification_helper/notification_activator.cc
@@ -77,7 +77,8 @@
   SHELLEXECUTEINFO info;
   memset(&info, 0, sizeof(info));
   info.cbSize = sizeof(info);
-  info.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_LOG_USAGE;
+  info.fMask =
+      SEE_MASK_NOASYNC | SEE_MASK_FLAG_LOG_USAGE | SEE_MASK_NOCLOSEPROCESS;
   info.lpFile = chrome_exe_path.value().c_str();
   info.lpParameters = params.c_str();
   info.nShow = SW_SHOWNORMAL;
@@ -88,6 +89,18 @@
     return HRESULT_FROM_WIN32(error_code);
   }
 
+  if (info.hProcess != NULL) {
+    DWORD pid = ::GetProcessId(info.hProcess);
+    if (!::AllowSetForegroundWindow(pid)) {
+      DWORD error_code = ::GetLastError();
+      Trace(L"Unable to forward activation privilege; error: 0x%08X\n",
+            error_code);
+      return HRESULT_FROM_WIN32(error_code);
+    }
+
+    CloseHandle(info.hProcess);
+  }
+
   return S_OK;
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cb14bd7..5bea1ed 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -728,7 +728,6 @@
       "../browser/spellchecker/spellcheck_service_browsertest.cc",
       "../browser/ssl/certificate_reporting_test_utils.cc",
       "../browser/ssl/certificate_reporting_test_utils.h",
-      "../browser/ssl/certificate_transparency_browsertest.cc",
       "../browser/ssl/chrome_expect_ct_reporter_browsertest.cc",
       "../browser/ssl/chrome_ssl_host_state_delegate_test.cc",
       "../browser/ssl/connection_help_tab_helper_browsertest.cc",
@@ -2996,7 +2995,6 @@
       "../browser/ui/global_error/global_error_service_unittest.cc",
       "../browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc",
       "../browser/ui/omnibox/clipboard_utils_unittest.cc",
-      "../browser/ui/omnibox/favicon_cache_unittest.cc",
       "../browser/ui/page_info/permission_menu_model_unittest.cc",
       "../browser/ui/passwords/manage_passwords_bubble_model_unittest.cc",
       "../browser/ui/passwords/password_dialog_controller_impl_unittest.cc",
@@ -3078,7 +3076,6 @@
 
     deps += [
       "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
-      "//components/favicon/core/test:test_support",
       "//components/signin/core/browser:signin_buildflags",
       "//services/metrics/public/cpp:ukm_builders",
       "//third_party/libaddressinput",
diff --git a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
index af310b63..b3c0877 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
@@ -547,7 +547,7 @@
       newwindow.contentWindow.onload = function(evt) {
         var newwebview =
             newwindow.contentWindow.document.querySelector('webview');
-        newwebview.request.onBeforeRequest.addListener(function(e) {
+        newwebview.request.onCompleted.addListener(function(e) {
           if (!calledWebRequestEvent) {
             calledWebRequestEvent = true;
             newwebview.parentElement.removeChild(newwebview);
diff --git a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
index 4d0a69d..d8f4cf70 100644
--- a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
@@ -8,7 +8,7 @@
 
   suiteSetup(function() {
     return PolymerTest.importHtml(
-        'chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html');
+        'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html');
   });
 
   setup(function() {
@@ -18,7 +18,7 @@
           <cr-lazy-render id="lazy">
             <template>
               <h1>
-                <paper-checkbox checked="{{checked}}"></paper-checkbox>
+                <cr-checkbox checked="{{checked}}"></cr-checkbox>
                 {{name}}
               </h1>
             </template>
@@ -51,7 +51,7 @@
     bind.checked = true;
 
     const inner = lazy.get();
-    const checkbox = document.querySelector('paper-checkbox');
+    const checkbox = document.querySelector('cr-checkbox');
     assertTrue(checkbox.checked);
     MockInteractions.tap(checkbox);
     assertFalse(checkbox.checked);
diff --git a/chrome/test/data/webui/extensions/extension_kiosk_mode_test.js b/chrome/test/data/webui/extensions/extension_kiosk_mode_test.js
index 18a9de6..24cefac 100644
--- a/chrome/test/data/webui/extensions/extension_kiosk_mode_test.js
+++ b/chrome/test/data/webui/extensions/extension_kiosk_mode_test.js
@@ -104,7 +104,7 @@
             expectTrue(items[1].querySelector('paper-button').hidden);
             // Bailout checkbox should be hidden when auto-launch editing
             // disabled.
-            expectTrue(dialog.$$('paper-checkbox').hidden);
+            expectTrue(dialog.$$('cr-checkbox').hidden);
 
             MockInteractions.tap(
                 items[0].querySelector('.icon-delete-gray button'));
@@ -156,14 +156,14 @@
       let bailoutCheckbox;
       return initPage()
           .then(() => {
-            bailoutCheckbox = dialog.$$('paper-checkbox');
+            bailoutCheckbox = dialog.$$('cr-checkbox');
             // Bailout checkbox should be usable when auto-launching.
             expectFalse(bailoutCheckbox.hidden);
             expectFalse(bailoutCheckbox.disabled);
             expectFalse(bailoutCheckbox.checked);
 
             // Making sure canceling doesn't change anything.
-            bailoutCheckbox.dispatchEvent(new PointerEvent('pointerdown'));
+            bailoutCheckbox.click();
             Polymer.dom.flush();
             expectTrue(dialog.$['confirm-dialog'].open);
 
@@ -175,7 +175,7 @@
             expectTrue(dialog.$.dialog.open);
 
             // Accepting confirmation dialog should trigger browserProxy call.
-            bailoutCheckbox.dispatchEvent(new PointerEvent('pointerdown'));
+            bailoutCheckbox.click();
             Polymer.dom.flush();
             expectTrue(dialog.$['confirm-dialog'].open);
 
@@ -192,7 +192,7 @@
 
             // Test clicking on checkbox again should simply re-enable bailout.
             browserProxy.reset();
-            bailoutCheckbox.dispatchEvent(new PointerEvent('pointerdown'));
+            bailoutCheckbox.click();
             expectFalse(bailoutCheckbox.checked);
             expectFalse(dialog.$['confirm-dialog'].open);
             return browserProxy.whenCalled('setDisableBailoutShortcut');
diff --git a/chrome/test/data/webui/extensions/extension_pack_dialog_test.js b/chrome/test/data/webui/extensions/extension_pack_dialog_test.js
index b5cf222..6f737d3 100644
--- a/chrome/test/data/webui/extensions/extension_pack_dialog_test.js
+++ b/chrome/test/data/webui/extensions/extension_pack_dialog_test.js
@@ -82,6 +82,7 @@
         expectEquals(kRootPath, packDialog.packDirectory_);
       }));
 
+      Polymer.dom.flush();
       expectEquals('', packDialog.$$('#key-file').value);
       MockInteractions.tap(packDialog.$$('#key-file-browse'));
       expectTrue(!!mockDelegate.keyPromise);
diff --git a/chrome/test/data/webui/md_bookmarks/edit_dialog_test.js b/chrome/test/data/webui/md_bookmarks/edit_dialog_test.js
index 12955bd..74520c6 100644
--- a/chrome/test/data/webui/md_bookmarks/edit_dialog_test.js
+++ b/chrome/test/data/webui/md_bookmarks/edit_dialog_test.js
@@ -71,6 +71,7 @@
 
     dialog.titleValue_ = 'Permission Site';
     dialog.urlValue_ = 'permission.site';
+    Polymer.dom.flush();
 
     MockInteractions.tap(dialog.$.saveButton);
 
@@ -87,7 +88,9 @@
     assertTrue(dialog.validateUrl_());
 
     dialog.urlValue_ = 'example.com';
+    Polymer.dom.flush();
     assertTrue(dialog.validateUrl_());
+    Polymer.dom.flush();
     assertEquals('http://example.com', dialog.urlValue_);
 
     dialog.urlValue_ = '';
@@ -103,7 +106,9 @@
 
     dialog.urlValue_ = '';
 
+    Polymer.dom.flush();
     MockInteractions.tap(dialog.$.saveButton);
+    Polymer.dom.flush();
 
     assertTrue(dialog.$.url.invalid);
     assertTrue(dialog.$.dialog.open);
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.js b/chrome/test/data/webui/print_preview/destination_search_test.js
new file mode 100644
index 0000000..16404ea
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/destination_search_test.js
@@ -0,0 +1,216 @@
+// 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.
+
+cr.define('destination_search_test', function() {
+  /** @enum {string} */
+  const TestNames = {
+    ReceiveSuccessfulSetup: 'receive successful setup',
+    ResolutionFails: 'resolution fails',
+    ReceiveFailedSetup: 'receive failed setup',
+    GetCapabilitiesFails: 'get capabilities fails',
+    CloudKioskPrinter: 'cloud kiosk printer',
+  };
+
+  const suiteName = 'NewDestinationSearchTest';
+  suite(suiteName, function() {
+    /** @type {?PrintPreviewDestinationDialogElement} */
+    let dialog = null;
+
+    /** @type {?print_preview.DestinationStore} */
+    let destinationStore = null;
+
+    /** @type {?print_preview.UserInfo} */
+    let userInfo = null;
+
+    /** @type {?print_preview.NativeLayer} */
+    let nativeLayer = null;
+
+    /** @override */
+    setup(function() {
+      // Create data classes
+      nativeLayer = new print_preview.NativeLayerStub();
+      print_preview.NativeLayer.setInstance(nativeLayer);
+      userInfo = new print_preview.UserInfo();
+      destinationStore = new print_preview.DestinationStore(
+          userInfo, new WebUIListenerTracker());
+      nativeLayer.setLocalDestinationCapabilities(
+          print_preview_test_utils.getCddTemplate('FooDevice', 'FooName'));
+      destinationStore.init(
+          false /* isInAppKioskMode */,
+          'FooDevice' /* printerName */,
+          '' /* serializedDefaultDestinationSelectionRulesStr */,
+          [] /* recentDestinations */);
+
+      // Set up dialog
+      dialog = document.createElement('print-preview-destination-dialog');
+      dialog.userInfo = userInfo;
+      dialog.destinationStore = destinationStore;
+      dialog.invitationStore = new print_preview.InvitationStore(userInfo);
+      dialog.recentDestinations = [];
+      PolymerTest.clearBody();
+      document.body.appendChild(dialog);
+      return nativeLayer.whenCalled('getPrinterCapabilities').then(function() {
+        dialog.show();
+        Polymer.dom.flush();
+        nativeLayer.reset();
+      });
+    });
+
+    /**
+     * @param {!print_preview.Destination} destination The destination to
+     *     simulate selection of.
+     */
+    function simulateDestinationSelect(destination) {
+      // Fake destinationListItem.
+      const item =
+          document.createElement('print-preview-destination-list-item');
+      item.destination = destination;
+
+      // Get print list and fire event.
+      const list = dialog.shadowRoot.querySelectorAll(
+          'print-preview-destination-list')[1];
+      list.fire('destination-selected', item);
+    }
+
+    /**
+     * Adds a destination to the dialog and simulates selection of the
+     * destination.
+     * @param {string} destId The ID for the destination.
+     */
+    function requestSetup(destId) {
+      const origin = cr.isChromeOS ? print_preview.DestinationOrigin.CROS :
+                                   print_preview.DestinationOrigin.LOCAL;
+
+      const dest = new print_preview.Destination(destId,
+          print_preview.DestinationType.LOCAL,
+          origin,
+          'displayName',
+          print_preview.DestinationConnectionStatus.ONLINE);
+
+      // Add the destination to the list.
+      dialog.updateDestinations_([dest]);
+      simulateDestinationSelect(dest);
+    }
+
+    // Tests that a destination is selected if the user clicks on it and setup
+    // (for CrOS) or capabilities fetch (for non-Cros) succeeds.
+    test(assert(TestNames.ReceiveSuccessfulSetup), function() {
+      const destId = '00112233DEADBEEF';
+      const response = {
+        printerId: destId,
+        capabilities:
+            print_preview_test_utils.getCddTemplate(destId).capabilities,
+        success: true,
+      };
+      if (cr.isChromeOS) {
+        nativeLayer.setSetupPrinterResponse(false, response);
+      } else {
+        nativeLayer.setLocalDestinationCapabilities(
+            print_preview_test_utils.getCddTemplate(destId));
+      }
+
+      const waiter = test_util.eventToPromise(
+          print_preview.DestinationStore.EventType.DESTINATION_SELECT,
+          destinationStore);
+      requestSetup(destId);
+      const callback =
+          cr.isChromeOS ? 'setupPrinter' : 'getPrinterCapabilities';
+      return Promise.all([nativeLayer.whenCalled(callback), waiter]).then(
+          function(results) {
+            const actualId =
+                cr.isChromeOS ? results[0] : results[0].destinationId;
+            assertEquals(destId, actualId);
+            // After setup or capabilities fetch succeeds, the destination
+            // should be selected.
+            assertNotEquals(null, destinationStore.selectedDestination);
+            assertEquals(destId, destinationStore.selectedDestination.id);
+          });
+    });
+
+    // Test what happens when the setupPrinter request is rejected. ChromeOS
+    // only.
+    test(assert(TestNames.ResolutionFails), function() {
+      const destId = '001122DEADBEEF';
+      const originalDestination = destinationStore.selectedDestination;
+      nativeLayer.setSetupPrinterResponse(true, {printerId: destId,
+                                                 success: false});
+      requestSetup(destId);
+      return nativeLayer.whenCalled('setupPrinter').then(
+          function(actualId) {
+            assertEquals(destId, actualId);
+            // The selected printer should not have changed, since a printer
+            // cannot be selected until setup succeeds.
+            assertEquals(originalDestination,
+                         destinationStore.selectedDestination);
+          });
+    });
+
+    // Test what happens when the setupPrinter request is resolved with a
+    // failed status. Chrome OS only.
+    test(assert(TestNames.ReceiveFailedSetup), function() {
+      const originalDestination = destinationStore.selectedDestination;
+      const destId = '00112233DEADBEEF';
+      const response = {
+        printerId: destId,
+        capabilities:
+            print_preview_test_utils.getCddTemplate(destId).capabilities,
+        success: false,
+      };
+      nativeLayer.setSetupPrinterResponse(false, response);
+      requestSetup(destId);
+      return nativeLayer.whenCalled('setupPrinter').then(
+          function (actualDestId) {
+            assertEquals(destId, actualDestId);
+            // The selected printer should not have changed, since a printer
+            // cannot be selected until setup succeeds.
+            assertEquals(originalDestination,
+                         destinationStore.selectedDestination);
+          });
+    });
+
+    // Tests what happens when capabilities cannot be retrieved for the chosen
+    // destination. The destination will still be selected in this case.
+    // non-Chrome OS only.
+    test(assert(TestNames.GetCapabilitiesFails), function() {
+      const destId = '001122DEADBEEF';
+      nativeLayer.setLocalDestinationCapabilities(
+          print_preview_test_utils.getCddTemplate(destId), true);
+      requestSetup(destId);
+      return nativeLayer.whenCalled('getPrinterCapabilities').then(
+          function(args) {
+            assertEquals(destId, args.destinationId);
+            // The destination is selected even though capabilities cannot be
+            // retrieved.
+            assertEquals(destId, destinationStore.selectedDestination.id);
+          });
+    });
+
+    // Test what happens when a simulated cloud kiosk printer is selected.
+    test(assert(TestNames.CloudKioskPrinter), function() {
+      const printerId = 'cloud-printer-id';
+
+      // Create cloud destination.
+      const cloudDest = new print_preview.Destination(printerId,
+          print_preview.DestinationType.GOOGLE,
+          print_preview.DestinationOrigin.DEVICE,
+          'displayName',
+          print_preview.DestinationConnectionStatus.ONLINE);
+      cloudDest.capabilities =
+          print_preview_test_utils.getCddTemplate(printerId, 'displayName')
+              .capabilities;
+
+      // Place destination in the local list as happens for Kiosk printers.
+      dialog.updateDestinations_([cloudDest]);
+      simulateDestinationSelect(cloudDest);
+
+      // Verify that the destination has been selected.
+      assertEquals(printerId, destinationStore.selectedDestination.id);
+    });
+  });
+
+  return {
+    suiteName: suiteName,
+    TestNames: TestNames,
+  };
+});
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 7c6d3bd..8ad4b12f 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -174,6 +174,37 @@
   this.runMochaTest(settings_select_test.TestNames.CustomMediaNames);
 });
 
+PrintPreviewPagesSettingsTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/pages_settings.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      'print_preview_test_utils.js',
+      'pages_settings_test.js',
+    ]);
+  }
+
+  /** @override */
+  get suiteName() {
+    return pages_settings_test.suiteName;
+  }
+};
+
+TEST_F('PrintPreviewPagesSettingsTest', 'ValidPageRanges',
+       function() {
+  this.runMochaTest(pages_settings_test.TestNames.ValidPageRanges);
+});
+
+TEST_F('PrintPreviewPagesSettingsTest', 'InvalidPageRanges',
+       function() {
+  this.runMochaTest(pages_settings_test.TestNames.InvalidPageRanges);
+});
+
 PrintPreviewRestoreStateTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
@@ -575,3 +606,55 @@
        function() {
   this.runMochaTest(custom_margins_test.TestNames.ControlsCheck);
 });
+
+PrintPreviewNewDestinationSearchTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/destination_dialog.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      ROOT_PATH + 'chrome/test/data/webui/settings/test_util.js',
+      ROOT_PATH + 'ui/webui/resources/js/webui_listener_tracker.js',
+      '../test_browser_proxy.js',
+      'native_layer_stub.js',
+      'print_preview_test_utils.js',
+      'destination_search_test.js',
+    ]);
+  }
+
+  /** @override */
+  get suiteName() {
+    return destination_search_test.suiteName;
+  }
+};
+
+TEST_F('PrintPreviewNewDestinationSearchTest', 'ReceiveSuccessfulSetup',
+       function() {
+  this.runMochaTest(destination_search_test.TestNames.ReceiveSuccessfulSetup);
+});
+
+GEN('#if defined(OS_CHROMEOS)');
+TEST_F('PrintPreviewNewDestinationSearchTest', 'ResolutionFails',
+       function() {
+  this.runMochaTest(destination_search_test.TestNames.ResolutionFails);
+});
+
+TEST_F('PrintPreviewNewDestinationSearchTest', 'ReceiveFailedSetup',
+       function() {
+  this.runMochaTest(destination_search_test.TestNames.ReceiveFailedSetup);
+});
+
+GEN('#else');  // !defined(OS_CHROMEOS)
+TEST_F('PrintPreviewNewDestinationSearchTest', 'GetCapabilitiesFails',
+       function() {
+  this.runMochaTest(destination_search_test.TestNames.GetCapabilitiesFails);
+});
+GEN('#endif');  // defined(OS_CHROMEOS)
+
+TEST_F('PrintPreviewNewDestinationSearchTest', 'CloudKioskPrinter',
+       function() {
+  this.runMochaTest(destination_search_test.TestNames.CloudKioskPrinter);
+});
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
new file mode 100644
index 0000000..7bfefd0
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -0,0 +1,159 @@
+// 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.
+
+cr.define('pages_settings_test', function() {
+  /** @enum {string} */
+  const TestNames = {
+    ValidPageRanges: 'valid page ranges',
+    InvalidPageRanges: 'invalid page ranges',
+  };
+
+  const suiteName = 'PagesSettingsTest';
+  suite(suiteName, function() {
+    /** @type {?PrintPreviewPagesSettingsElement} */
+    let pagesSection = null;
+
+    /** @type {?print_preview.DocumentInfo} */
+    let documentInfo = null;
+
+    /** @override */
+    setup(function() {
+      documentInfo = new print_preview.DocumentInfo();
+      documentInfo.init(true, 'title', false);
+
+      PolymerTest.clearBody();
+      pagesSection = document.createElement('print-preview-pages-settings');
+      pagesSection.settings = {
+        pages: {
+          value: [1],
+          unavailableValue: [],
+          valid: true,
+          available: true,
+          key: '',
+        },
+        ranges: {
+          value: [],
+          unavailableValue: [],
+          valid: true,
+          available: true,
+          key: '',
+        },
+      };
+      pagesSection.documentInfo = documentInfo;
+      pagesSection.disabled = false;
+      document.body.appendChild(pagesSection);
+    });
+
+    /**
+     * Sets up the pages section to use the custom input with the input string
+     * given by |inputString|, with the document page count set to |pageCount|
+     * @param {string} inputString
+     * @param {number} pageCount
+     * @return {!Promise} Promise that resolves when the input-change event
+     *     has fired.
+     */
+    function setupInput(inputString, pageCount) {
+      // Set page count.
+      documentInfo.updatePageCount(pageCount);
+      pagesSection.notifyPath('documentInfo.pageCount');
+      Polymer.dom.flush();
+
+      // Select custom
+      pagesSection.$$('#custom-radio-button').checked = true;
+      pagesSection.$$('#all-radio-button').dispatchEvent(
+          new CustomEvent('change'));
+
+      // Set input string
+      const input = pagesSection.$.pageSettingsCustomInput;
+      input.value = inputString;
+      input.dispatchEvent(new CustomEvent('input'));
+
+      // Validate results
+      return test_util.eventToPromise('input-change', pagesSection);
+    }
+
+    // Tests that the page ranges set are valid for different user inputs.
+    test(assert(TestNames.ValidPageRanges), function() {
+      /** @param {!Array<number>} expectedPages The expected pages value. */
+      const validateState = function(expectedPages) {
+        const pagesValue = pagesSection.getSettingValue('pages');
+        assertEquals(expectedPages.length, pagesValue.length);
+        expectedPages.forEach((page, index) => {
+          assertEquals(page, pagesValue[index]);
+        });
+        assertTrue(pagesSection.$$('.hint').hidden);
+      };
+
+      const oneToHundred = Array.from({length: 100}, (x, i) => i + 1);
+      const tenToHundred = Array.from({length: 91}, (x, i) => i + 10);
+
+      return setupInput('1, 2, 3, 1, 56', 100).then(function() {
+        validateState([1, 2, 3, 56]);
+        return setupInput('1-3, 6-9, 6-10', 100);
+      }).then(function() {
+        validateState([1, 2, 3, 6, 7, 8, 9, 10]);
+        return setupInput('10-', 100);
+      }).then(function() {
+        validateState(tenToHundred);
+        return setupInput('10-100', 100);
+      }).then(function() {
+        validateState(tenToHundred);
+        return setupInput('-', 100);
+      }).then(function() {
+        validateState(oneToHundred);
+        // https://crbug.com/806165
+        return setupInput('1\u30012\u30013\u30011\u300156', 100);
+      }).then(function() {
+        validateState([1, 2, 3, 56]);
+        return setupInput('1,2,3\u30011\u300156', 100);
+      }).then(function() {
+        validateState([1, 2, 3, 56]);
+      });
+    });
+
+    // Tests that the correct error messages are shown for different user
+    // inputs.
+    test(assert(TestNames.InvalidPageRanges), function() {
+      const limitError = 'Out of bounds page reference, limit is ';
+      const syntaxError = 'Invalid page range, use e.g. 1-5, 8, 11-13';
+
+      /** @param {string} expectedMessage The expected error message. */
+      const validateState = function(expectedMessage) {
+        assertFalse(pagesSection.$$('.hint').hidden);
+        assertEquals(expectedMessage,
+                     pagesSection.$$('.hint').textContent.trim());
+      };
+
+      return setupInput('10-100000', 100).then(function() {
+        validateState(limitError + '100');
+        return setupInput('1, 100000', 100);
+      }).then(function() {
+        validateState(limitError + '100');
+        return setupInput('1, 2, 0, 56', 100);
+      }).then(function() {
+        validateState(syntaxError);
+        return setupInput('-1, 1, 2,, 56', 100);
+      }).then(function() {
+        validateState(syntaxError);
+        return setupInput('1,2,56-40', 100);
+      }).then(function() {
+        validateState(syntaxError);
+        return setupInput('101-110', 100);
+      }).then(function() {
+        validateState(limitError + '100');
+        return setupInput('1\u30012\u30010\u300156', 100);
+      }).then(function() {
+        validateState(syntaxError);
+        return setupInput('-1,1,2\u3001\u300156', 100);
+      }).then(function() {
+        validateState(syntaxError);
+      });
+    });
+  });
+
+  return {
+    suiteName: suiteName,
+    TestNames: TestNames,
+  };
+});
diff --git a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
index eecd1e5c..cf3d23b 100644
--- a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
@@ -73,6 +73,7 @@
         testElement = document.createElement('settings-password-prompt-dialog');
         testElement.quickUnlockPrivate_ = quickUnlockPrivateApi;
         testElement.writeUma_ = fakeUma.recordProgress.bind(fakeUma);
+        Polymer.dom.flush();
         document.body.appendChild(testElement);
 
         passwordElement = getFromElement('#passwordInput');
@@ -99,13 +100,16 @@
         const confirmButton = getFromElement('#passwordInput');
         quickUnlockPrivateApi.accountPassword = 'bar';
         passwordElement.value = 'foo';
+        Polymer.dom.flush();
+
         MockInteractions.tap(
             getFromElement('paper-button[class="action-button"]'));
 
-        assertEquals(0, passwordElement.inputElement.selectionStart);
+        assertEquals(
+            0, passwordElement.inputElement.inputElement.selectionStart);
         assertEquals(
             passwordElement.value.length,
-            passwordElement.inputElement.selectionEnd);
+            passwordElement.inputElement.inputElement.selectionEnd);
       });
 
       test('TapConfirmButtonWithWrongPasswordRestoresFocus', function() {
diff --git a/chrome/test/data/webui/settings/search_engines_page_test.js b/chrome/test/data/webui/settings/search_engines_page_test.js
index c2a75dd..f1254b0c 100644
--- a/chrome/test/data/webui/settings/search_engines_page_test.js
+++ b/chrome/test/data/webui/settings/search_engines_page_test.js
@@ -112,13 +112,16 @@
               Promise.resolve();
         };
 
-        assertEquals('', dialog.$.searchEngine.value);
-        assertEquals('', dialog.$.keyword.value);
-        assertEquals('', dialog.$.queryUrl.value);
         const actionButton = dialog.$.actionButton;
-        assertTrue(actionButton.disabled);
 
-        return inputAndValidate('searchEngine')
+        return browserProxy.whenCalled('searchEngineEditStarted')
+            .then(() => {
+              assertEquals('', dialog.$.searchEngine.value);
+              assertEquals('', dialog.$.keyword.value);
+              assertEquals('', dialog.$.queryUrl.value);
+              assertTrue(actionButton.disabled);
+            })
+            .then(() => inputAndValidate('searchEngine'))
             .then(() => inputAndValidate('keyword'))
             .then(() => inputAndValidate('queryUrl'))
             .then(() => {
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index 37986260..3f22e158 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -291,7 +291,7 @@
   function openActionMenu(index) {
     const item = testElement.$.listContainer.children[index];
     const dots = item.querySelector('#actionMenuButton');
-    MockInteractions.tap(dots);
+    dots.click();
     Polymer.dom.flush();
   }
 
@@ -569,7 +569,7 @@
           // Select 'Remove' from menu.
           const remove = testElement.$.reset;
           assertTrue(!!remove);
-          MockInteractions.tap(remove);
+          remove.click();
           return browserProxy.whenCalled('resetCategoryPermissionForPattern');
         })
         .then(function(args) {
@@ -607,7 +607,7 @@
           openActionMenu(1);
           const remove = testElement.$.reset;
           assertTrue(!!remove);
-          MockInteractions.tap(remove);
+          remove.click();
           return browserProxy.whenCalled('resetCategoryPermissionForPattern');
         })
         .then(function(args) {
@@ -649,7 +649,7 @@
           assertTrue(!!resetButton);
           assertFalse(resetButton.hidden);
 
-          MockInteractions.tap(resetButton.querySelector('button'));
+          resetButton.querySelector('button').click();
           return browserProxy.whenCalled('resetCategoryPermissionForPattern');
         })
         .then(function(args) {
@@ -673,7 +673,7 @@
       assertTrue(menu.open);
       const edit = testElement.$.edit;
       assertTrue(!!edit);
-      MockInteractions.tap(edit);
+      edit.click();
       Polymer.dom.flush();
       assertFalse(menu.open);
 
@@ -681,6 +681,30 @@
     });
   });
 
+  test('edit dialog closes when incognito status changes', function() {
+    setUpCategory(
+        settings.ContentSettingsTypes.COOKIES, settings.ContentSetting.BLOCK,
+        prefsSessionOnly);
+
+    return browserProxy.whenCalled('getExceptionList')
+        .then(function() {
+          Polymer.dom.flush();  // Populates action menu.
+
+          openActionMenu(0);
+          testElement.$.edit.click();
+          Polymer.dom.flush();
+
+          const dialog = testElement.$$('settings-edit-exception-dialog');
+          assertTrue(!!dialog);
+          const closeEventPromise = test_util.eventToPromise('close', dialog);
+          browserProxy.setIncognito(true);
+          return closeEventPromise;
+        })
+        .then(() => {
+          assertFalse(!!testElement.$$('settings-edit-exception-dialog'));
+        });
+  });
+
   test('list items shown and clickable when data is present', function() {
     const contentType = settings.ContentSettingsTypes.GEOLOCATION;
     setUpCategory(contentType, settings.ContentSetting.ALLOW, prefsGeolocation);
@@ -705,7 +729,7 @@
           const firstItem = testElement.$.listContainer.children[0];
           const clickable = firstItem.querySelector('.middle');
           assertTrue(!!clickable);
-          MockInteractions.tap(clickable);
+          clickable.click();
           assertEquals(
               prefsGeolocation.exceptions[contentType][0].origin,
               settings.getQueryParameters().get('site'));
@@ -831,7 +855,7 @@
           openActionMenu(0);
           const allow = testElement.$.allow;
           assertTrue(!!allow);
-          MockInteractions.tap(allow);
+          allow.click();
           return browserProxy.whenCalled('setCategoryPermissionForPattern');
         });
   });
@@ -848,7 +872,7 @@
 
           const allow = testElement.$.allow;
           assertTrue(!!allow);
-          MockInteractions.tap(allow);
+          allow.click();
           return browserProxy.whenCalled('setCategoryPermissionForPattern');
         })
         .then(function(args) {
@@ -933,7 +957,7 @@
     assertTrue(!!actionButton);
     assertFalse(actionButton.disabled);
 
-    MockInteractions.tap(actionButton);
+    actionButton.click();
     return browserProxy.whenCalled('resetCategoryPermissionForPattern')
         .then(function(args) {
           assertEquals(cookieException.origin, args[0]);
diff --git a/chrome/test/data/webui/settings/startup_urls_page_test.js b/chrome/test/data/webui/settings/startup_urls_page_test.js
index 73b6765..30245bd8 100644
--- a/chrome/test/data/webui/settings/startup_urls_page_test.js
+++ b/chrome/test/data/webui/settings/startup_urls_page_test.js
@@ -85,6 +85,7 @@
 
     test('Initialization_Add', function() {
       document.body.appendChild(dialog);
+      Polymer.dom.flush();
       assertTrue(dialog.$.dialog.open);
 
       // Assert that the "Add" button is disabled.
diff --git a/chrome/test/media_router/media_router_base_browsertest.h b/chrome/test/media_router/media_router_base_browsertest.h
index ab63b78..3ac0db9c 100644
--- a/chrome/test/media_router/media_router_base_browsertest.h
+++ b/chrome/test/media_router/media_router_base_browsertest.h
@@ -28,7 +28,7 @@
  * 2. "--extension-unpacked" flag to specify the unpacked extension location
  * Only one of them should be passed when run browser tests.
  */
-class MediaRouterBaseBrowserTest : public ExtensionBrowserTest,
+class MediaRouterBaseBrowserTest : public extensions::ExtensionBrowserTest,
                                    public extensions::ProcessManagerObserver {
  public:
   MediaRouterBaseBrowserTest();
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index dc9f124f9..96eb7eb 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1250,7 +1250,7 @@
 }
 
 #if BUILDFLAG(ENABLE_NACL)
-class PackagedAppTest : public ExtensionBrowserTest {
+class PackagedAppTest : public extensions::ExtensionBrowserTest {
  public:
   explicit PackagedAppTest(const std::string& toolchain)
       : toolchain_(toolchain) { }
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 64434aa..8c9091df 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -506,11 +506,13 @@
 
   if (enable_chromecast_extensions) {
     sources += [
+      "$root_gen_dir/chromecast/renderer/extensions_renderer_resources.pak",
       "$root_gen_dir/extensions/extensions_renderer_resources.pak",
       "$root_gen_dir/extensions/extensions_resources.pak",
       "$root_gen_dir/extensions/shell/app_shell_resources.pak",
     ]
     deps += [
+      "//chromecast/renderer:extensions_resources",
       "//extensions:extensions_resources",
       "//extensions/shell:resources",
     ]
diff --git a/chromecast/android/AndroidManifest.xml b/chromecast/android/AndroidManifest.xml
deleted file mode 100644
index bd606188..0000000
--- a/chromecast/android/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
--->
-
-<!--
-  This is a dummy manifest which is required by lint for determining min/target
-  SDK version.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="dummy.package">
-
-    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="22" />
-
-</manifest>
diff --git a/chromecast/android/src/dummy b/chromecast/android/src/dummy
deleted file mode 100644
index c1069e01..0000000
--- a/chromecast/android/src/dummy
+++ /dev/null
@@ -1,2 +0,0 @@
-Note(gunsch): This file is for the cast_shell_apk target in chromecast.gyp.
-See the notes above that target's 'java_in_dir' variable in chromecast.gyp.
diff --git a/chromecast/base/chromecast_switches.cc b/chromecast/base/chromecast_switches.cc
index b53a3f1..8bb660308 100644
--- a/chromecast/base/chromecast_switches.cc
+++ b/chromecast/base/chromecast_switches.cc
@@ -149,6 +149,11 @@
 // a valid origin for the top or bottom swipe gesture.
 const char kSystemGestureStartHeight[] = "system-gesture-start-height";
 
+// The number of pixels from the start of a left swipe gesture to consider as a
+// 'back' gesture.
+const char kBackGestureHorizontalThreshold[] =
+    "back-gesture-horizontal-threshold";
+
 }  // namespace switches
 
 namespace chromecast {
diff --git a/chromecast/base/chromecast_switches.h b/chromecast/base/chromecast_switches.h
index e511f0e..740bbb8a 100644
--- a/chromecast/base/chromecast_switches.h
+++ b/chromecast/base/chromecast_switches.h
@@ -77,6 +77,7 @@
 extern const char kEnableInput[];
 extern const char kSystemGestureStartWidth[];
 extern const char kSystemGestureStartHeight[];
+extern const char kBackGestureHorizontalThreshold[];
 
 // Background color used when Chromium hasn't rendered anything yet.
 extern const char kCastAppBackgroundColor[];
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index f223f240..753238e 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -19,6 +19,8 @@
     "application_media_capabilities.h",
     "bluetooth/cast_bluetooth_chooser.cc",
     "bluetooth/cast_bluetooth_chooser.h",
+    "cast_back_gesture_dispatcher.cc",
+    "cast_back_gesture_dispatcher.h",
     "cast_browser_context.cc",
     "cast_browser_context.h",
     "cast_browser_main_parts.cc",
@@ -377,6 +379,7 @@
 cast_source_set("browsertests") {
   testonly = true
   sources = [
+    "cast_back_gesture_dispatcher_test.cc",
     "cast_media_blocker_browsertest.cc",
     "renderer_prelauncher_test.cc",
     "test/cast_features_browsertest.cc",
diff --git a/chromecast/browser/cast_back_gesture_dispatcher.cc b/chromecast/browser/cast_back_gesture_dispatcher.cc
new file mode 100644
index 0000000..fbb74aba
--- /dev/null
+++ b/chromecast/browser/cast_back_gesture_dispatcher.cc
@@ -0,0 +1,50 @@
+// 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 "chromecast/browser/cast_back_gesture_dispatcher.h"
+
+#include "chromecast/base/chromecast_switches.h"
+
+namespace chromecast {
+namespace shell {
+
+namespace {
+constexpr int kDefaultBackGestureHorizontalThreshold = 80;
+}  // namespace
+
+CastBackGestureDispatcher::CastBackGestureDispatcher(
+    CastContentWindow::Delegate* delegate)
+    : horizontal_threshold_(
+          GetSwitchValueInt(switches::kBackGestureHorizontalThreshold,
+                            kDefaultBackGestureHorizontalThreshold)),
+      delegate_(delegate),
+      dispatched_back_(false) {
+  DCHECK(delegate_);
+}
+bool CastBackGestureDispatcher::CanHandleSwipe(
+    CastSideSwipeOrigin swipe_origin) {
+  return swipe_origin == CastSideSwipeOrigin::LEFT &&
+         delegate_->CanHandleGesture(GestureType::GO_BACK);
+}
+
+void CastBackGestureDispatcher::HandleSideSwipeBegin(
+    CastSideSwipeOrigin swipe_origin,
+    const gfx::Point& touch_location) {
+  if (swipe_origin == CastSideSwipeOrigin::LEFT) {
+    dispatched_back_ = false;
+  }
+}
+
+void CastBackGestureDispatcher::HandleSideSwipeContinue(
+    CastSideSwipeOrigin swipe_origin,
+    const gfx::Point& touch_location) {
+  if (!dispatched_back_ && swipe_origin == CastSideSwipeOrigin::LEFT &&
+      touch_location.x() >= horizontal_threshold_) {
+    dispatched_back_ = true;
+    delegate_->ConsumeGesture(GestureType::GO_BACK);
+  }
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_back_gesture_dispatcher.h b/chromecast/browser/cast_back_gesture_dispatcher.h
new file mode 100644
index 0000000..6a651f9b
--- /dev/null
+++ b/chromecast/browser/cast_back_gesture_dispatcher.h
@@ -0,0 +1,39 @@
+// 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 CHROMECAST_BROWSER_CAST_BACK_GESTURE_DISPATCHER_H_
+#define CHROMECAST_BROWSER_CAST_BACK_GESTURE_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "chromecast/browser/cast_content_window.h"
+#include "chromecast/graphics/cast_side_swipe_gesture_handler.h"
+
+namespace chromecast {
+namespace shell {
+
+// Takes side swipe gestures destined for implementations of
+// CastContentWindow and dispatches them to a CastContentWindow::Delegate if the
+// side swipe is a back gesture.
+class CastBackGestureDispatcher : public CastSideSwipeGestureHandlerInterface {
+ public:
+  explicit CastBackGestureDispatcher(CastContentWindow::Delegate* delegate);
+
+  // CastSideSwipeGestureHandlerInterface implementation:
+  bool CanHandleSwipe(CastSideSwipeOrigin swipe_origin) override;
+  void HandleSideSwipeBegin(CastSideSwipeOrigin swipe_origin,
+                            const gfx::Point& touch_location) override;
+  void HandleSideSwipeContinue(CastSideSwipeOrigin swipe_origin,
+                               const gfx::Point& touch_location) override;
+
+ private:
+  // Number of pixels past swipe origin to consider as a back gesture.
+  const int horizontal_threshold_;
+  CastContentWindow::Delegate* const delegate_;
+  bool dispatched_back_;
+};
+
+}  // namespace shell
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_BACK_GESTURE_DISPATCHER_H_
diff --git a/chromecast/browser/cast_back_gesture_dispatcher_test.cc b/chromecast/browser/cast_back_gesture_dispatcher_test.cc
new file mode 100644
index 0000000..726f34d
--- /dev/null
+++ b/chromecast/browser/cast_back_gesture_dispatcher_test.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 "chromecast/browser/cast_back_gesture_dispatcher.h"
+
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_base.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+// Gmock matchers and actions that we use below.
+using testing::AnyOf;
+using testing::Eq;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::WithArg;
+using testing::_;
+
+namespace chromecast {
+namespace shell {
+
+class MockCastContentWindowDelegate : public CastContentWindow::Delegate {
+ public:
+  ~MockCastContentWindowDelegate() override = default;
+
+  MOCK_METHOD1(CanHandleGesture, bool(GestureType gesture_type));
+  MOCK_METHOD1(ConsumeGesture, bool(GestureType gesture_type));
+  std::string GetId() override { return "mockContentWindowDelegate"; }
+};
+
+// Verify the simple case of a left swipe with the right horizontal leads to
+// back.
+TEST(CastBackGestureDispatcherTest, VerifySimpleDispatchSuccess) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  EXPECT_CALL(delegate, CanHandleGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  EXPECT_CALL(delegate, ConsumeGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::LEFT);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::LEFT, gfx::Point(5, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(90, 50));
+}
+
+// Verify that multiple 'continue' events still only lead to one back
+// invocation.
+TEST(CastBackGestureDispatcherTest, VerifyOnlySingleDispatch) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  EXPECT_CALL(delegate, CanHandleGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  EXPECT_CALL(delegate, ConsumeGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::LEFT);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::LEFT, gfx::Point(5, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(90, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(105, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(200, 50));
+}
+
+// Verify that if the delegate says it doesn't handle back that we won't try to
+// ask them to consume it.
+TEST(CastBackGestureDispatcherTest, VerifyDelegateDoesNotConsumeUnwanted) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  EXPECT_CALL(delegate, CanHandleGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(false));
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::LEFT);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::LEFT, gfx::Point(5, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(90, 50));
+}
+
+// Verify that a not-left gesture doesn't lead to a swipe.
+TEST(CastBackGestureDispatcherTest, VerifyNotLeftSwipeIsNotBack) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::TOP);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::TOP, gfx::Point(0, 5));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::TOP,
+                                     gfx::Point(0, 90));
+}
+
+// Verify that if the gesture doesn't go far enough horizontally that we will
+// not consider it a swipe.
+TEST(CastBackGestureDispatcherTest, VerifyNotFarEnoughRightIsNotBack) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  EXPECT_CALL(delegate, CanHandleGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::LEFT);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::LEFT, gfx::Point(5, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(70, 50));
+}
+
+// Verify that if the gesture ends before going far enough, that's also not a
+// swipe.
+TEST(CastBackGestureDispatcherTest, VerifyNotFarEnoughRightAndEndIsNotBack) {
+  MockCastContentWindowDelegate delegate;
+  CastBackGestureDispatcher dispatcher(&delegate);
+  EXPECT_CALL(delegate, CanHandleGesture(Eq(GestureType::GO_BACK)))
+      .WillOnce(Return(true));
+  dispatcher.CanHandleSwipe(CastSideSwipeOrigin::LEFT);
+  dispatcher.HandleSideSwipeBegin(CastSideSwipeOrigin::LEFT, gfx::Point(5, 50));
+  dispatcher.HandleSideSwipeContinue(CastSideSwipeOrigin::LEFT,
+                                     gfx::Point(70, 50));
+  dispatcher.HandleSideSwipeEnd(CastSideSwipeOrigin::LEFT, gfx::Point(75, 50));
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window_aura.cc b/chromecast/browser/cast_content_window_aura.cc
index 4a762b8..859cc6ff 100644
--- a/chromecast/browser/cast_content_window_aura.cc
+++ b/chromecast/browser/cast_content_window_aura.cc
@@ -77,9 +77,9 @@
 CastContentWindowAura::CastContentWindowAura(
     CastContentWindow::Delegate* delegate,
     bool is_touch_enabled)
-    : delegate_(delegate), is_touch_enabled_(is_touch_enabled) {
-  DCHECK(delegate_);
-}
+    : back_gesture_dispatcher_(
+          std::make_unique<CastBackGestureDispatcher>(delegate)),
+      is_touch_enabled_(is_touch_enabled) {}
 
 CastContentWindowAura::~CastContentWindowAura() {
   if (window_manager_)
@@ -121,16 +121,20 @@
 void CastContentWindowAura::RequestMoveOut(){};
 
 bool CastContentWindowAura::CanHandleSwipe(CastSideSwipeOrigin swipe_origin) {
-  return swipe_origin == CastSideSwipeOrigin::LEFT &&
-         delegate_->CanHandleGesture(GestureType::GO_BACK);
+  return back_gesture_dispatcher_->CanHandleSwipe(swipe_origin);
 }
 
 void CastContentWindowAura::HandleSideSwipeBegin(
     CastSideSwipeOrigin swipe_origin,
     const gfx::Point& touch_location) {
-  if (swipe_origin == CastSideSwipeOrigin::LEFT) {
-    delegate_->ConsumeGesture(GestureType::GO_BACK);
-  }
+  back_gesture_dispatcher_->HandleSideSwipeBegin(swipe_origin, touch_location);
+}
+
+void CastContentWindowAura::HandleSideSwipeContinue(
+    CastSideSwipeOrigin swipe_origin,
+    const gfx::Point& touch_location) {
+  back_gesture_dispatcher_->HandleSideSwipeContinue(swipe_origin,
+                                                    touch_location);
 }
 
 }  // namespace shell
diff --git a/chromecast/browser/cast_content_window_aura.h b/chromecast/browser/cast_content_window_aura.h
index 555aba1..cd376d57 100644
--- a/chromecast/browser/cast_content_window_aura.h
+++ b/chromecast/browser/cast_content_window_aura.h
@@ -6,6 +6,7 @@
 #define CHROMECAST_BROWSER_CAST_CONTENT_WINDOW_AURA_H_
 
 #include "base/macros.h"
+#include "chromecast/browser/cast_back_gesture_dispatcher.h"
 #include "chromecast/browser/cast_content_window.h"
 #include "chromecast/graphics/cast_side_swipe_gesture_handler.h"
 
@@ -38,6 +39,8 @@
   bool CanHandleSwipe(CastSideSwipeOrigin swipe_origin) override;
   void HandleSideSwipeBegin(CastSideSwipeOrigin swipe_origin,
                             const gfx::Point& touch_location) override;
+  void HandleSideSwipeContinue(CastSideSwipeOrigin swipe_origin,
+                               const gfx::Point& touch_location) override;
 
  private:
   friend class CastContentWindow;
@@ -45,7 +48,9 @@
   // This class should only be instantiated by CastContentWindow::Create.
   CastContentWindowAura(Delegate* delegate, bool is_touch_enabled);
 
-  Delegate* const delegate_;
+  // Utility class for detecting and dispatching back gestures to delegates.
+  std::unique_ptr<CastBackGestureDispatcher> back_gesture_dispatcher_;
+
   const bool is_touch_enabled_;
   std::unique_ptr<TouchBlocker> touch_blocker_;
 
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index 8a8822a..b893bc0 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -61,20 +61,6 @@
 
 const char kCookieStoreFile[] = "Cookies";
 
-// A CTPolicyEnforcer that accepts all certificates.
-class IgnoresCTPolicyEnforcer : public net::CTPolicyEnforcer {
- public:
-  IgnoresCTPolicyEnforcer() = default;
-  ~IgnoresCTPolicyEnforcer() override = default;
-
-  net::ct::CTPolicyCompliance CheckCompliance(
-      net::X509Certificate* cert,
-      const net::SCTList& verified_scts,
-      const net::NetLogWithSource& net_log) override {
-    return net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-};
-
 bool IgnoreCertificateErrors() {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   return cmd_line->HasSwitch(switches::kIgnoreCertificateErrors);
@@ -247,7 +233,7 @@
   transport_security_state_.reset(new net::TransportSecurityState());
   // Certificate transparency is current disabled for Chromecast.
   cert_transparency_verifier_.reset(new net::DoNothingCTVerifier());
-  ct_policy_enforcer_.reset(new IgnoresCTPolicyEnforcer());
+  ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer());
 
   http_auth_handler_factory_ =
       net::HttpAuthHandlerFactory::CreateDefault(host_resolver_.get());
diff --git a/chromecast/public/avsettings.h b/chromecast/public/avsettings.h
index deac339..0a8f013 100644
--- a/chromecast/public/avsettings.h
+++ b/chromecast/public/avsettings.h
@@ -136,6 +136,9 @@
 
     // This event should be fired when an HDMI error occurs.
     HDMI_ERROR = 102,
+
+    // This event should be fired when the display brightness is changed.
+    DISPLAY_BRIGHTNESS_CHANGED = 200,
   };
 
   // Delegate to inform the caller events. As a subclass of TaskRunner,
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 4c40c3bb..e7c83e7 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -3,6 +3,26 @@
 # found in the LICENSE file.
 
 import("//chromecast/chromecast.gni")
+import("//tools/grit/grit_rule.gni")
+
+grit("extensions_resources") {
+  source = "resources/extensions_renderer_resources.grd"
+  output_dir = "$root_gen_dir/chromecast/renderer"
+  output_name = "extensions_renderer_resources"
+  outputs = [
+    "grit/extensions_renderer_resources.h",
+    "extensions_renderer_resources.pak",
+  ]
+  grit_flags = [
+    "-E",
+    "mojom_root=" + rebase_path(root_gen_dir, root_build_dir),
+  ]
+  deps = [
+    "//net/interfaces:interfaces_js",
+    "//url/mojom:url_mojom_gurl_js",
+    "//url/mojom:url_mojom_origin_js",
+  ]
+}
 
 cast_source_set("renderer") {
   sources = [
diff --git a/chromecast/renderer/resources/extensions/automation/automation_event.js b/chromecast/renderer/resources/extensions/automation/automation_event.js
new file mode 100644
index 0000000..88c94d15
--- /dev/null
+++ b/chromecast/renderer/resources/extensions/automation/automation_event.js
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+var utils = require('utils');
+
+function AutomationEventImpl(type, target, eventFrom) {
+  this.propagationStopped = false;
+  this.type = type;
+  this.target = target;
+  this.eventPhase = Event.NONE;
+  this.eventFrom = eventFrom;
+}
+
+AutomationEventImpl.prototype = {
+  __proto__: null,
+  stopPropagation: function() {
+    this.propagationStopped = true;
+  },
+};
+
+function AutomationEvent() {
+  privates(AutomationEvent).constructPrivate(this, arguments);
+}
+utils.expose(AutomationEvent, AutomationEventImpl, {
+  functions: [
+    'stopPropagation',
+  ],
+  readonly: [
+    'type',
+    'target',
+    'eventPhase',
+    'eventFrom',
+  ],
+});
+
+exports.$set('AutomationEvent', AutomationEvent);
diff --git a/chromecast/renderer/resources/extensions/automation/automation_node.js b/chromecast/renderer/resources/extensions/automation/automation_node.js
new file mode 100644
index 0000000..06ff7049
--- /dev/null
+++ b/chromecast/renderer/resources/extensions/automation/automation_node.js
@@ -0,0 +1,1466 @@
+// Copyright 2014 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.
+
+var AutomationEvent = require('automationEvent').AutomationEvent;
+var automationInternal =
+    getInternalApi ?
+        getInternalApi('automationInternal') :
+        require('binding').Binding.create('automationInternal').generate();
+var exceptionHandler = require('uncaught_exception_handler');
+
+var natives = requireNative('automationInternal');
+
+var IsInteractPermitted = natives.IsInteractPermitted;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The id of the root node.
+ */
+var GetRootID = natives.GetRootID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?string} The title of the document.
+ */
+var GetDocTitle = natives.GetDocTitle;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?string} The url of the document.
+ */
+var GetDocURL = natives.GetDocURL;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?boolean} True if the document has finished loading.
+ */
+var GetDocLoaded = natives.GetDocLoaded;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded).
+ */
+var GetDocLoadingProgress =
+    natives.GetDocLoadingProgress;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The ID of the selection anchor object.
+ */
+var GetAnchorObjectID = natives.GetAnchorObjectID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The selection anchor offset.
+ */
+var GetAnchorOffset = natives.GetAnchorOffset;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?string} The selection anchor affinity.
+ */
+var GetAnchorAffinity = natives.GetAnchorAffinity;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The ID of the selection focus object.
+ */
+var GetFocusObjectID = natives.GetFocusObjectID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?number} The selection focus offset.
+ */
+var GetFocusOffset = natives.GetFocusOffset;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @return {?string} The selection focus affinity.
+ */
+var GetFocusAffinity = natives.GetFocusAffinity;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The id of the node's parent, or undefined if it's the
+ *    root of its tree or if the tree or node wasn't found.
+ */
+var GetParentID = natives.GetParentID;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The number of children of the node, or undefined if
+ *     the tree or node wasn't found.
+ */
+var GetChildCount = natives.GetChildCount;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {number} childIndex An index of a child of this node.
+ * @return {?number} The id of the child at the given index, or undefined
+ *     if the tree or node or child at that index wasn't found.
+ */
+var GetChildIDAtIndex = natives.GetChildIDAtIndex;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The ids of the children of the node, or undefined
+ *     if the tree or node wasn't found.
+ */
+var GetChildIds = natives.GetChildIDs;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?Object} An object mapping html attributes to values.
+ */
+var GetHtmlAttributes = natives.GetHtmlAttributes;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?number} The index of this node in its parent, or undefined if
+ *     the tree or node or node parent wasn't found.
+ */
+var GetIndexInParent = natives.GetIndexInParent;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?Object} An object with a string key for every state flag set,
+ *     or undefined if the tree or node or node parent wasn't found.
+ */
+var GetState = natives.GetState;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {string} The restriction, one of
+ * "disabled", "readOnly" or undefined if enabled or other object not disabled
+ */
+var GetRestriction = natives.GetRestriction;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {string} The checked state, as undefined, "true", "false" or "mixed".
+ */
+var GetChecked = natives.GetChecked;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {string} The role of the node, or undefined if the tree or
+ *     node wasn't found.
+ */
+var GetRole = natives.GetRole;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?automation.Rect} The location of the node, or undefined if
+ *     the tree or node wasn't found.
+ */
+var GetLocation = natives.GetLocation;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {number} startIndex The start index of the range.
+ * @param {number} endIndex The end index of the range.
+ * @return {?automation.Rect} The bounding box of the subrange of this node,
+ *     or the location if there are no subranges, or undefined if
+ *     the tree or node wasn't found.
+ */
+var GetBoundsForRange = natives.GetBoundsForRange;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?automation.Rect} The unclipped location of the node, or
+ * undefined if the tree or node wasn't found.
+ */
+var GetUnclippedLocation = natives.GetUnclippedLocation;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {!Array<number>} The text offset where each line starts, or an empty
+ *     array if this node has no text content, or undefined if the tree or node
+ *     was not found.
+ */
+var GetLineStartOffsets = requireNative(
+    'automationInternal').GetLineStartOffsets;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of a string attribute.
+ * @return {?string} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetStringAttribute = natives.GetStringAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?boolean} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetBoolAttribute = natives.GetBoolAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?number} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetIntAttribute = natives.GetIntAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?Array<number>} The ids of nodes who have a relationship pointing
+ *     to |nodeID| (a reverse relationship).
+ */
+var GetIntAttributeReverseRelations =
+    natives.GetIntAttributeReverseRelations;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?number} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetFloatAttribute = natives.GetFloatAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?Array<number>} The value of this attribute, or undefined
+ *     if the tree, node, or attribute wasn't found.
+ */
+var GetIntListAttribute =
+    natives.GetIntListAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an attribute.
+ * @return {?Array<number>} The ids of nodes who have a relationship pointing
+ *     to |nodeID| (a reverse relationship).
+ */
+var GetIntListAttributeReverseRelations =
+    natives.GetIntListAttributeReverseRelations;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @param {string} attr The name of an HTML attribute.
+ * @return {?string} The value of this attribute, or undefined if the tree,
+ *     node, or attribute wasn't found.
+ */
+var GetHtmlAttribute = natives.GetHtmlAttribute;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {automation.NameFromType} The source of the node's name.
+ */
+var GetNameFrom = natives.GetNameFrom;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {boolean}
+ */
+var GetBold = natives.GetBold;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {boolean}
+ */
+var GetItalic = natives.GetItalic;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {boolean}
+ */
+var GetUnderline = natives.GetUnderline;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {boolean}
+ */
+var GetLineThrough = natives.GetLineThrough;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?Array<automation.CustomAction>} List of custom actions of the
+ *     node.
+ */
+var GetCustomActions = natives.GetCustomActions;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {?Array<string>} List of standard actions of the node.
+ */
+var GetStandardActions = natives.GetStandardActions;
+
+/**
+ * @param {number} axTreeID The id of the accessibility tree.
+ * @param {number} nodeID The id of a node.
+ * @return {automation.NameFromType} The source of the node's name.
+ */
+var GetDefaultActionVerb = natives.GetDefaultActionVerb;
+
+var logging = requireNative('logging');
+var utils = require('utils');
+
+/**
+ * A single node in the Automation tree.
+ * @param {AutomationRootNodeImpl} root The root of the tree.
+ * @constructor
+ */
+function AutomationNodeImpl(root) {
+  this.rootImpl = root;
+  this.hostNode_ = null;
+  this.listeners = {__proto__: null};
+}
+
+AutomationNodeImpl.prototype = {
+  __proto__: null,
+  treeID: -1,
+  id: -1,
+  isRootNode: false,
+
+  detach: function() {
+    this.rootImpl = null;
+    this.hostNode_ = null;
+    this.listeners = {__proto__: null};
+  },
+
+  get root() {
+    return this.rootImpl && this.rootImpl.wrapper;
+  },
+
+  get parent() {
+    if (!this.rootImpl)
+      return undefined;
+    if (this.hostNode_)
+      return this.hostNode_;
+    var parentID = GetParentID(this.treeID, this.id);
+    return this.rootImpl.get(parentID);
+  },
+
+  get htmlAttributes() {
+    return GetHtmlAttributes(this.treeID, this.id) || {};
+  },
+
+  get state() {
+    return GetState(this.treeID, this.id) || {};
+  },
+
+  get role() {
+    return GetRole(this.treeID, this.id);
+  },
+
+  get restriction() {
+    return GetRestriction(this.treeID, this.id);
+  },
+
+  get checked() {
+    return GetChecked(this.treeID, this.id);
+  },
+
+  get location() {
+    return GetLocation(this.treeID, this.id);
+  },
+
+  boundsForRange: function(startIndex, endIndex) {
+    return GetBoundsForRange(this.treeID, this.id, startIndex, endIndex);
+  },
+
+  get unclippedLocation() {
+    var result = GetUnclippedLocation(this.treeID, this.id);
+    if (result === undefined)
+      result = GetLocation(this.treeID, this.id);
+    return result;
+  },
+
+  get indexInParent() {
+    return GetIndexInParent(this.treeID, this.id);
+  },
+
+  get lineStartOffsets() {
+    return GetLineStartOffsets(this.treeID, this.id);
+  },
+
+  get childTree() {
+    var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
+    if (childTreeID)
+      return AutomationRootNodeImpl.get(childTreeID);
+  },
+
+  get firstChild() {
+    if (!this.rootImpl)
+      return undefined;
+    if (this.childTree)
+      return this.childTree;
+    if (!GetChildCount(this.treeID, this.id))
+      return undefined;
+    var firstChildID = GetChildIDAtIndex(this.treeID, this.id, 0);
+    return this.rootImpl.get(firstChildID);
+  },
+
+  get lastChild() {
+    if (!this.rootImpl)
+      return undefined;
+    if (this.childTree)
+      return this.childTree;
+    var count = GetChildCount(this.treeID, this.id);
+    if (!count)
+      return undefined;
+    var lastChildID = GetChildIDAtIndex(this.treeID, this.id, count - 1);
+    return this.rootImpl.get(lastChildID);
+  },
+
+  get children() {
+    if (!this.rootImpl)
+      return [];
+
+    if (this.childTree)
+      return [this.childTree];
+
+    var children = [];
+    var childIds = GetChildIds(this.treeID, this.id);
+    for (var i = 0; i < childIds.length; ++i) {
+      var childID = childIds[i];
+      var child = this.rootImpl.get(childID);
+      $Array.push(children, child);
+    }
+    return children;
+  },
+
+  get previousSibling() {
+    var parent = this.parent;
+    if (!parent)
+      return undefined;
+    parent = privates(parent).impl;
+    var indexInParent = GetIndexInParent(this.treeID, this.id);
+    return this.rootImpl.get(
+        GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1));
+  },
+
+  get nextSibling() {
+    var parent = this.parent;
+    if (!parent)
+      return undefined;
+    parent = privates(parent).impl;
+    var indexInParent = GetIndexInParent(this.treeID, this.id);
+    return this.rootImpl.get(
+        GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1));
+  },
+
+  get nameFrom() {
+    return GetNameFrom(this.treeID, this.id);
+  },
+
+  get bold() {
+    return GetBold(this.treeID, this.id);
+  },
+
+  get italic() {
+    return GetItalic(this.treeID, this.id);
+  },
+
+  get underline() {
+    return GetUnderline(this.treeID, this.id);
+  },
+
+  get lineThrough() {
+    return GetLineThrough(this.treeID, this.id);
+  },
+
+  get customActions() {
+    return GetCustomActions(this.treeID, this.id);
+  },
+
+  get standardActions() {
+    return GetStandardActions(this.treeID, this.id);
+  },
+
+  get defaultActionVerb() {
+    return GetDefaultActionVerb(this.treeID, this.id);
+  },
+
+  doDefault: function() {
+    this.performAction_('doDefault');
+  },
+
+  focus: function() {
+    this.performAction_('focus');
+  },
+
+  getImageData: function(maxWidth, maxHeight) {
+    this.performAction_('getImageData',
+                        { maxWidth: maxWidth,
+                          maxHeight: maxHeight });
+  },
+
+  hitTest: function(x, y, eventToFire) {
+    this.hitTestInternal(x, y, eventToFire);
+  },
+
+  hitTestWithReply: function(x, y, opt_callback) {
+    this.hitTestInternal(x, y, 'hitTestResult', opt_callback);
+  },
+
+  hitTestInternal: function(x, y, eventToFire, opt_callback) {
+    // Convert from global to tree-relative coordinates.
+    var location = GetLocation(this.treeID, GetRootID(this.treeID));
+    this.performAction_('hitTest',
+                        { x: Math.floor(x - location.left),
+                          y: Math.floor(y - location.top),
+                          eventToFire: eventToFire },
+                        opt_callback);
+  },
+
+  makeVisible: function() {
+    this.performAction_('scrollToMakeVisible');
+  },
+
+  performCustomAction: function(customActionId) {
+    this.performAction_('customAction', { customActionID: customActionId });
+  },
+
+  performStandardAction: function(action) {
+    var standardActions = GetStandardActions(this.treeID, this.id);
+    if (!standardActions ||
+        !standardActions.find(item => action == item)) {
+      throw 'Inapplicable action for node: ' + action;
+    }
+    this.performAction_(action);
+  },
+
+  replaceSelectedText: function(value) {
+    if (this.state.editable) {
+      this.performAction_('replaceSelectedText', { value: value});
+    }
+  },
+
+  resumeMedia: function() {
+    this.performAction_('resumeMedia');
+  },
+
+  scrollBackward: function(opt_callback) {
+    this.performAction_('scrollBackward', {}, opt_callback);
+  },
+
+  scrollForward: function(opt_callback) {
+    this.performAction_('scrollForward', {}, opt_callback);
+  },
+
+  scrollUp: function(opt_callback) {
+    this.performAction_('scrollUp', {}, opt_callback);
+  },
+
+  scrollDown: function(opt_callback) {
+    this.performAction_('scrollDown', {}, opt_callback);
+  },
+
+  scrollLeft: function(opt_callback) {
+    this.performAction_('scrollLeft', {}, opt_callback);
+  },
+
+  scrollRight: function(opt_callback) {
+    this.performAction_('scrollRight', {}, opt_callback);
+  },
+
+  setSelection: function(startIndex, endIndex) {
+    if (this.state.editable) {
+      this.performAction_('setSelection',
+                          { focusNodeID: this.id,
+                            anchorOffset: startIndex,
+                            focusOffset: endIndex });
+    }
+  },
+
+  setSequentialFocusNavigationStartingPoint: function() {
+    this.performAction_('setSequentialFocusNavigationStartingPoint');
+  },
+
+  setValue: function(value) {
+    if (this.state.editable) {
+      this.performAction_('setValue', { value: value});
+    }
+  },
+
+  showContextMenu: function() {
+    this.performAction_('showContextMenu');
+  },
+
+  startDuckingMedia: function() {
+    this.performAction_('startDuckingMedia');
+  },
+
+  stopDuckingMedia: function() {
+    this.performAction_('stopDuckingMedia');
+  },
+
+  suspendMedia: function() {
+    this.performAction_('suspendMedia');
+  },
+
+  domQuerySelector: function(selector, callback) {
+    if (!this.rootImpl)
+      callback();
+    automationInternal.querySelector(
+      { treeID: this.rootImpl.treeID,
+        automationNodeID: this.id,
+        selector: selector },
+      $Function.bind(this.domQuerySelectorCallback_, this, callback));
+  },
+
+  find: function(params) {
+    return this.findInternal_(params);
+  },
+
+  findAll: function(params) {
+    return this.findInternal_(params, []);
+  },
+
+  matches: function(params) {
+    return this.matchInternal_(params);
+  },
+
+  addEventListener: function(eventType, callback, capture) {
+    this.removeEventListener(eventType, callback);
+    if (!this.listeners[eventType])
+      this.listeners[eventType] = [];
+    $Array.push(this.listeners[eventType], {
+      __proto__: null,
+      callback: callback,
+      capture: !!capture,
+    });
+  },
+
+  // TODO(dtseng/aboxhall): Check this impl against spec.
+  removeEventListener: function(eventType, callback) {
+    if (this.listeners[eventType]) {
+      var listeners = this.listeners[eventType];
+      for (var i = 0; i < listeners.length; i++) {
+        if (callback === listeners[i].callback)
+          $Array.splice(listeners, i, 1);
+      }
+    }
+  },
+
+  toJSON: function() {
+    return { treeID: this.treeID,
+             id: this.id,
+             role: this.role,
+             attributes: this.attributes };
+  },
+
+  dispatchEvent: function(eventType, eventFrom, mouseX, mouseY) {
+    var path = [];
+    var parent = this.parent;
+    while (parent) {
+      $Array.push(path, parent);
+      parent = parent.parent;
+    }
+    var event = new AutomationEvent(eventType, this.wrapper, eventFrom);
+    event.mouseX = mouseX;
+    event.mouseY = mouseY;
+
+    // Dispatch the event through the propagation path in three phases:
+    // - capturing: starting from the root and going down to the target's parent
+    // - targeting: dispatching the event on the target itself
+    // - bubbling: starting from the target's parent, going back up to the root.
+    // At any stage, a listener may call stopPropagation() on the event, which
+    // will immediately stop event propagation through this path.
+    if (this.dispatchEventAtCapturing_(event, path)) {
+      if (this.dispatchEventAtTargeting_(event, path))
+        this.dispatchEventAtBubbling_(event, path);
+    }
+  },
+
+  toString: function() {
+    var parentID = GetParentID(this.treeID, this.id);
+    var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
+    var count = GetChildCount(this.treeID, this.id);
+    var childIDs = [];
+    for (var i = 0; i < count; ++i) {
+      var childID = GetChildIDAtIndex(this.treeID, this.id, i);
+      $Array.push(childIDs, childID);
+    }
+
+    var result = 'node id=' + this.id +
+        ' role=' + this.role +
+        ' state=' + $JSON.stringify(this.state) +
+        ' parentID=' + parentID +
+        ' childIds=' + $JSON.stringify(childIDs);
+    if (this.hostNode_) {
+      var hostNodeImpl = privates(this.hostNode_).impl;
+      result += ' host treeID=' + hostNodeImpl.treeID +
+          ' host nodeID=' + hostNodeImpl.id;
+    }
+    if (childTreeID)
+      result += ' childTreeID=' + childTreeID;
+    return result;
+  },
+
+  dispatchEventAtCapturing_: function(event, path) {
+    privates(event).impl.eventPhase = Event.CAPTURING_PHASE;
+    for (var i = path.length - 1; i >= 0; i--) {
+      this.fireEventListeners_(path[i], event);
+      if (privates(event).impl.propagationStopped)
+        return false;
+    }
+    return true;
+  },
+
+  dispatchEventAtTargeting_: function(event) {
+    privates(event).impl.eventPhase = Event.AT_TARGET;
+    this.fireEventListeners_(this.wrapper, event);
+    return !privates(event).impl.propagationStopped;
+  },
+
+  dispatchEventAtBubbling_: function(event, path) {
+    privates(event).impl.eventPhase = Event.BUBBLING_PHASE;
+    for (var i = 0; i < path.length; i++) {
+      this.fireEventListeners_(path[i], event);
+      if (privates(event).impl.propagationStopped)
+        return false;
+    }
+    return true;
+  },
+
+  fireEventListeners_: function(node, event) {
+    var nodeImpl = privates(node).impl;
+    if (!nodeImpl.rootImpl)
+      return;
+
+    var listeners = nodeImpl.listeners[event.type];
+    if (!listeners)
+      return;
+    var eventPhase = event.eventPhase;
+    for (var i = 0; i < listeners.length; i++) {
+      if (eventPhase == Event.CAPTURING_PHASE && !listeners[i].capture)
+        continue;
+      if (eventPhase == Event.BUBBLING_PHASE && listeners[i].capture)
+        continue;
+
+      try {
+        listeners[i].callback(event);
+      } catch (e) {
+        exceptionHandler.handle('Error in event handler for ' + event.type +
+            ' during phase ' + eventPhase, e);
+      }
+    }
+  },
+
+  performAction_: function(actionType, opt_args, opt_callback) {
+    if (!this.rootImpl)
+      return;
+
+    // Not yet initialized.
+    if (this.rootImpl.treeID === undefined ||
+        this.id === undefined) {
+      return;
+    }
+
+    // Check permissions.
+    if (!IsInteractPermitted()) {
+      throw new Error(actionType + ' requires {"desktop": true} or' +
+          ' {"interact": true} in the "automation" manifest key.');
+    }
+    var requestID = -1;
+    if (opt_callback) {
+      requestID = this.rootImpl.addActionResultCallback(opt_callback);
+    }
+
+    automationInternal.performAction({ treeID: this.rootImpl.treeID,
+                                       automationNodeID: this.id,
+                                       actionType: actionType,
+                                       requestID: requestID},
+                                     opt_args || {});
+  },
+
+  domQuerySelectorCallback_: function(userCallback, resultAutomationNodeID) {
+    // resultAutomationNodeID could be zero or undefined or (unlikely) null;
+    // they all amount to the same thing here, which is that no node was
+    // returned.
+    if (!resultAutomationNodeID || !this.rootImpl) {
+      userCallback(null);
+      return;
+    }
+    var resultNode = this.rootImpl.get(resultAutomationNodeID);
+    if (!resultNode) {
+      logging.WARNING('Query selector result not in tree: ' +
+                      resultAutomationNodeID);
+      userCallback(null);
+    }
+    userCallback(resultNode);
+  },
+
+  findInternal_: function(params, opt_results) {
+    var result = null;
+    this.forAllDescendants_(function(node) {
+      if (privates(node).impl.matchInternal_(params)) {
+        if (opt_results)
+          $Array.push(opt_results, node);
+        else
+          result = node;
+        return !opt_results;
+      }
+    });
+    if (opt_results)
+      return opt_results;
+    return result;
+  },
+
+  /**
+   * Executes a closure for all of this node's descendants, in pre-order.
+   * Early-outs if the closure returns true.
+   * @param {Function(AutomationNode):boolean} closure Closure to be executed
+   *     for each node. Return true to early-out the traversal.
+   */
+  forAllDescendants_: function(closure) {
+    var stack = $Array.reverse(this.wrapper.children);
+    while (stack.length > 0) {
+      var node = $Array.pop(stack);
+      if (closure(node))
+        return;
+
+      var children = node.children;
+      for (var i = children.length - 1; i >= 0; i--)
+        $Array.push(stack, children[i]);
+    }
+  },
+
+  matchInternal_: function(params) {
+    if ($Object.keys(params).length === 0)
+      return false;
+
+    if ('role' in params && this.role != params.role)
+        return false;
+
+    if ('state' in params) {
+      for (var state in params.state) {
+        if (params.state[state] != (state in this.state))
+          return false;
+      }
+    }
+    if ('attributes' in params) {
+      for (var attribute in params.attributes) {
+        var attrValue = params.attributes[attribute];
+        if (typeof attrValue != 'object') {
+          if (this[attribute] !== attrValue)
+            return false;
+        } else if (attrValue instanceof $RegExp.self) {
+          if (typeof this[attribute] != 'string')
+            return false;
+          if (!attrValue.test(this[attribute]))
+            return false;
+        } else {
+          // TODO(aboxhall): handle intlist case.
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+};
+
+var stringAttributes = [
+    'accessKey',
+    'ariaInvalidValue',
+    'autoComplete',
+    'className',
+    'containerLiveRelevant',
+    'containerLiveStatus',
+    'description',
+    'display',
+    'htmlTag',
+    'imageDataUrl',
+    'language',
+    'liveRelevant',
+    'liveStatus',
+    'name',
+    'placeholder',
+    'roleDescription',
+    'textInputType',
+    'url',
+    'value'];
+
+var boolAttributes = [
+    'busy',
+    'clickable',
+    'containerLiveAtomic',
+    'containerLiveBusy',
+    'liveAtomic',
+    'modal',
+    'scrollable',
+    'selected'
+];
+
+var intAttributes = [
+    'backgroundColor',
+    'color',
+    'colorValue',
+    'hierarchicalLevel',
+    'posInSet',
+    'scrollX',
+    'scrollXMax',
+    'scrollXMin',
+    'scrollY',
+    'scrollYMax',
+    'scrollYMin',
+    'setSize',
+    'tableCellColumnIndex',
+    'ariaCellColumnIndex',
+    'tableCellColumnSpan',
+    'tableCellRowIndex',
+    'ariaCellRowIndex',
+    'tableCellRowSpan',
+    'tableColumnCount',
+    'ariaColumnCount',
+    'tableColumnIndex',
+    'tableRowCount',
+    'ariaRowCount',
+    'tableRowIndex',
+    'textSelEnd',
+    'textSelStart'];
+
+// Int attribute, relation property to expose, reverse relation to expose.
+var nodeRefAttributes = [
+    ['activedescendantId', 'activeDescendant', null],
+    ['detailsId', 'details', 'detailsFor'],
+    ['errorMessageId', 'errorMessage', 'errorMessageFor'],
+    ['inPageLinkTargetId', 'inPageLinkTarget', null],
+    ['nextFocusId', 'nextFocus', null],
+    ['nextOnLineId', 'nextOnLine', null],
+    ['previousFocusId', 'previousFocus', null],
+    ['previousOnLineId', 'previousOnLine', null],
+    ['tableColumnHeaderId', 'tableColumnHeader', null],
+    ['tableHeaderId', 'tableHeader', null],
+    ['tableRowHeaderId', 'tableRowHeader', null]];
+
+var intListAttributes = [
+    'lineBreaks',
+    'markerEnds',
+    'markerStarts',
+    'markerTypes',
+    'wordEnds',
+    'wordStarts'];
+
+// Intlist attribute, relation property to expose, reverse relation to expose.
+var nodeRefListAttributes = [
+    ['controlsIds', 'controls', 'controlledBy'],
+    ['describedbyIds', 'describedBy', 'descriptionFor'],
+    ['flowtoIds', 'flowTo', 'flowFrom'],
+    ['labelledbyIds', 'labelledBy', 'labelFor']];
+
+var floatAttributes = [
+    'valueForRange',
+    'minValueForRange',
+    'maxValueForRange'];
+
+var htmlAttributes = [
+    ['type', 'inputType']];
+
+var publicAttributes = [];
+
+$Array.forEach(stringAttributes, function(attributeName) {
+  $Array.push(publicAttributes, attributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    __proto__: null,
+    get: function() {
+      return GetStringAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
+
+$Array.forEach(boolAttributes, function(attributeName) {
+  $Array.push(publicAttributes, attributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    __proto__: null,
+    get: function() {
+      return GetBoolAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
+
+$Array.forEach(intAttributes, function(attributeName) {
+  $Array.push(publicAttributes, attributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    __proto__: null,
+    get: function() {
+      return GetIntAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
+
+$Array.forEach(nodeRefAttributes, function(params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  var dstReverseAttributeName = params[2];
+  $Array.push(publicAttributes, dstAttributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    __proto__: null,
+    get: function() {
+      var id = GetIntAttribute(this.treeID, this.id, srcAttributeName);
+      if (id && this.rootImpl)
+        return this.rootImpl.get(id);
+      else
+        return undefined;
+    }
+  });
+  if (dstReverseAttributeName) {
+    $Array.push(publicAttributes, dstReverseAttributeName);
+    $Object.defineProperty(AutomationNodeImpl.prototype,
+                           dstReverseAttributeName, {
+      __proto__: null,
+      get: function() {
+        var ids = GetIntAttributeReverseRelations(
+            this.treeID, this.id, srcAttributeName);
+        if (!ids || !this.rootImpl)
+          return undefined;
+        var result = [];
+        for (var i = 0; i < ids.length; ++i) {
+          var node = this.rootImpl.get(ids[i]);
+          if (node)
+          $Array.push(result, node);
+        }
+        return result;
+      }
+    });
+  }
+});
+
+$Array.forEach(intListAttributes, function(attributeName) {
+  $Array.push(publicAttributes, attributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    __proto__: null,
+    get: function() {
+      return GetIntListAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
+
+$Array.forEach(nodeRefListAttributes, function(params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  var dstReverseAttributeName = params[2];
+  $Array.push(publicAttributes, dstAttributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    __proto__: null,
+    get: function() {
+      var ids = GetIntListAttribute(this.treeID, this.id, srcAttributeName);
+      if (!ids || !this.rootImpl)
+        return undefined;
+      var result = [];
+      for (var i = 0; i < ids.length; ++i) {
+        var node = this.rootImpl.get(ids[i]);
+        if (node)
+          $Array.push(result, node);
+      }
+      return result;
+    }
+  });
+  if (dstReverseAttributeName) {
+    $Array.push(publicAttributes, dstReverseAttributeName);
+    $Object.defineProperty(AutomationNodeImpl.prototype,
+                           dstReverseAttributeName, {
+      __proto__: null,
+      get: function() {
+        var ids = GetIntListAttributeReverseRelations(
+            this.treeID, this.id, srcAttributeName);
+        if (!ids || !this.rootImpl)
+          return undefined;
+        var result = [];
+        for (var i = 0; i < ids.length; ++i) {
+          var node = this.rootImpl.get(ids[i]);
+          if (node)
+          $Array.push(result, node);
+        }
+        return result;
+      }
+    });
+  }
+});
+
+$Array.forEach(floatAttributes, function(attributeName) {
+  $Array.push(publicAttributes, attributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
+    __proto__: null,
+    get: function() {
+      return GetFloatAttribute(this.treeID, this.id, attributeName);
+    }
+  });
+});
+
+$Array.forEach(htmlAttributes, function(params) {
+  var srcAttributeName = params[0];
+  var dstAttributeName = params[1];
+  $Array.push(publicAttributes, dstAttributeName);
+  $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
+    __proto__: null,
+    get: function() {
+      return GetHtmlAttribute(this.treeID, this.id, srcAttributeName);
+    }
+  });
+});
+
+/**
+ * AutomationRootNode.
+ *
+ * An AutomationRootNode is the javascript end of an AXTree living in the
+ * browser. AutomationRootNode handles unserializing incremental updates from
+ * the source AXTree. Each update contains node data that form a complete tree
+ * after applying the update.
+ *
+ * A brief note about ids used through this class. The source AXTree assigns
+ * unique ids per node and we use these ids to build a hash to the actual
+ * AutomationNode object.
+ * Thus, tree traversals amount to a lookup in our hash.
+ *
+ * The tree itself is identified by the accessibility tree id of the
+ * renderer widget host.
+ * @constructor
+ */
+function AutomationRootNodeImpl(treeID) {
+  $Function.call(AutomationNodeImpl, this, this);
+  this.treeID = treeID;
+  this.axNodeDataCache_ = {__proto__: null};
+  this.actionRequestIDToCallback_ = {__proto__: null};
+}
+
+utils.defineProperty(AutomationRootNodeImpl, 'idToAutomationRootNode_',
+    {__proto__: null});
+
+utils.defineProperty(AutomationRootNodeImpl, 'get', function(treeID) {
+  var result = AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+  return result || undefined;
+});
+
+utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) {
+  if (AutomationRootNodeImpl.idToAutomationRootNode_[treeID])
+    return AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+  var result = new AutomationRootNode(treeID);
+  AutomationRootNodeImpl.idToAutomationRootNode_[treeID] = result;
+  return result;
+});
+
+utils.defineProperty(AutomationRootNodeImpl, 'destroy', function(treeID) {
+  delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
+});
+
+AutomationRootNodeImpl.prototype = {
+  __proto__: AutomationNodeImpl.prototype,
+
+  /**
+   * @type {boolean}
+   */
+  isRootNode: true,
+
+  /**
+   * @type {number}
+   */
+  treeID: -1,
+
+  /**
+   * The parent of this node from a different tree.
+   * @type {?AutomationNode}
+   * @private
+   */
+  hostNode_: null,
+
+  /**
+   * A map from id to AutomationNode.
+   * @type {Object.<number, AutomationNode>}
+   * @private
+   */
+  axNodeDataCache_: null,
+
+  actionRequestCounter_: 0,
+
+  actionRequestIDToCallback_: null,
+
+  get id() {
+    var result = GetRootID(this.treeID);
+
+    // Don't return undefined, because the id is often passed directly
+    // as an argument to a native binding that expects only a valid number.
+    if (result === undefined)
+      return -1;
+
+    return result;
+  },
+
+  get chromeChannel() {
+    return GetStringAttribute(this.treeID, this.id, 'chromeChannel');
+  },
+
+  get docUrl() {
+    return GetDocURL(this.treeID);
+  },
+
+  get docTitle() {
+    return GetDocTitle(this.treeID);
+  },
+
+  get docLoaded() {
+    return GetDocLoaded(this.treeID);
+  },
+
+  get docLoadingProgress() {
+    return GetDocLoadingProgress(this.treeID);
+  },
+
+  get anchorObject() {
+    var id = GetAnchorObjectID(this.treeID);
+    if (id && id != -1)
+      return this.get(id);
+    else
+      return undefined;
+  },
+
+  get anchorOffset() {
+    var id = GetAnchorObjectID(this.treeID);
+    if (id && id != -1)
+      return GetAnchorOffset(this.treeID);
+  },
+
+  get anchorAffinity() {
+    var id = GetAnchorObjectID(this.treeID);
+    if (id && id != -1)
+      return GetAnchorAffinity(this.treeID);
+  },
+
+  get focusObject() {
+    var id = GetFocusObjectID(this.treeID);
+    if (id && id != -1)
+      return this.get(id);
+    else
+      return undefined;
+  },
+
+  get focusOffset() {
+    var id = GetFocusObjectID(this.treeID);
+    if (id && id != -1)
+      return GetFocusOffset(this.treeID);
+  },
+
+  get focusAffinity() {
+    var id = GetFocusObjectID(this.treeID);
+    if (id && id != -1)
+      return GetFocusAffinity(this.treeID);
+  },
+
+  get: function(id) {
+    if (id == undefined)
+      return undefined;
+
+    if (id == this.id)
+      return this.wrapper;
+
+    var obj = this.axNodeDataCache_[id];
+    if (obj)
+      return obj;
+
+    // Validate the backing AXTree has the specified node.
+    if (!GetRole(this.treeID, id))
+      return;
+
+    obj = new AutomationNode(this);
+    privates(obj).impl.treeID = this.treeID;
+    privates(obj).impl.id = id;
+    this.axNodeDataCache_[id] = obj;
+
+    return obj;
+  },
+
+  remove: function(id) {
+    if (this.axNodeDataCache_[id])
+      privates(this.axNodeDataCache_[id]).impl.detach();
+    delete this.axNodeDataCache_[id];
+  },
+
+  destroy: function() {
+    this.dispatchEvent('destroyed', 'none');
+    for (var id in this.axNodeDataCache_)
+      this.remove(id);
+    this.detach();
+  },
+
+  setHostNode(hostNode) {
+    this.hostNode_ = hostNode;
+  },
+
+  onAccessibilityEvent: function(eventParams) {
+    var targetNode = this.get(eventParams.targetID);
+    if (targetNode) {
+      var targetNodeImpl = privates(targetNode).impl;
+      targetNodeImpl.dispatchEvent(
+          eventParams.eventType, eventParams.eventFrom,
+          eventParams.mouseX, eventParams.mouseY);
+
+      if (eventParams.actionRequestID != -1) {
+        this.onActionResult(eventParams.actionRequestID, targetNode);
+      }
+    } else {
+      logging.WARNING('Got ' + eventParams.eventType +
+                      ' event on unknown node: ' + eventParams.targetID +
+                      '; this: ' + this.id);
+    }
+    return true;
+  },
+
+  addActionResultCallback: function(callback) {
+    this.actionRequestIDToCallback_[++this.actionRequestCounter_] = callback;
+    return this.actionRequestCounter_;
+  },
+
+  onActionResult: function(requestID, result) {
+    if (requestID in this.actionRequestIDToCallback_) {
+      this.actionRequestIDToCallback_[requestID](result);
+      delete this.actionRequestIDToCallback_[requestID];
+    }
+  },
+
+  toString: function() {
+    function toStringInternal(nodeImpl, indent) {
+      if (!nodeImpl)
+        return '';
+      var output = '';
+      if (nodeImpl.isRootNode)
+        output += indent + 'tree id=' + nodeImpl.treeID + '\n';
+      output += indent +
+        $Function.call(AutomationNodeImpl.prototype.toString, nodeImpl) + '\n';
+      indent += '  ';
+      var children = nodeImpl.children;
+      for (var i = 0; i < children.length; ++i)
+        output += toStringInternal(privates(children[i]).impl, indent);
+      return output;
+    }
+    return toStringInternal(this, '');
+  },
+};
+
+function AutomationNode() {
+  privates(AutomationNode).constructPrivate(this, arguments);
+}
+utils.expose(AutomationNode, AutomationNodeImpl, {
+  functions: [
+    'doDefault',
+    'find',
+    'findAll',
+    'focus',
+    'getImageData',
+    'hitTest',
+    'hitTestWithReply',
+    'makeVisible',
+    'matches',
+    'performCustomAction',
+    'performStandardAction',
+    'replaceSelectedText',
+    'resumeMedia',
+    'scrollBackward',
+    'scrollForward',
+    'scrollUp',
+    'scrollDown',
+    'scrollLeft',
+    'scrollRight',
+    'setSelection',
+    'setSequentialFocusNavigationStartingPoint',
+    'setValue',
+    'showContextMenu',
+    'startDuckingMedia',
+    'stopDuckingMedia',
+    'suspendMedia',
+    'addEventListener',
+    'removeEventListener',
+    'domQuerySelector',
+    'toString',
+    'boundsForRange',
+  ],
+  readonly: $Array.concat(publicAttributes, [
+      'parent',
+      'firstChild',
+      'lastChild',
+      'children',
+      'previousSibling',
+      'nextSibling',
+      'isRootNode',
+      'role',
+      'checked',
+      'defaultActionVerb',
+      'restriction',
+      'state',
+      'location',
+      'indexInParent',
+      'lineStartOffsets',
+      'root',
+      'htmlAttributes',
+      'nameFrom',
+      'bold',
+      'italic',
+      'underline',
+      'lineThrough',
+      'customActions',
+      'standardActions',
+      'unclippedLocation',
+  ]),
+});
+
+function AutomationRootNode() {
+  privates(AutomationRootNode).constructPrivate(this, arguments);
+}
+utils.expose(AutomationRootNode, AutomationRootNodeImpl, {
+  superclass: AutomationNode,
+  readonly: [
+    'chromeChannel',
+    'docTitle',
+    'docUrl',
+    'docLoaded',
+    'docLoadingProgress',
+    'anchorObject',
+    'anchorOffset',
+    'anchorAffinity',
+    'focusObject',
+    'focusOffset',
+    'focusAffinity',
+  ],
+});
+
+utils.defineProperty(AutomationRootNode, 'get', function(treeID) {
+  return AutomationRootNodeImpl.get(treeID);
+});
+
+utils.defineProperty(AutomationRootNode, 'getOrCreate', function(treeID) {
+  return AutomationRootNodeImpl.getOrCreate(treeID);
+});
+
+utils.defineProperty(AutomationRootNode, 'destroy', function(treeID) {
+  AutomationRootNodeImpl.destroy(treeID);
+});
+
+exports.$set('AutomationNode', AutomationNode);
+exports.$set('AutomationRootNode', AutomationRootNode);
diff --git a/chromecast/renderer/resources/extensions/automation_custom_bindings.js b/chromecast/renderer/resources/extensions/automation_custom_bindings.js
new file mode 100644
index 0000000..8ec1c9c
--- /dev/null
+++ b/chromecast/renderer/resources/extensions/automation_custom_bindings.js
@@ -0,0 +1,373 @@
+// Copyright 2014 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.
+
+// Custom bindings for the automation API.
+var AutomationNode = require('automationNode').AutomationNode;
+var AutomationRootNode = require('automationNode').AutomationRootNode;
+var automation = apiBridge || require('binding').Binding.create('automation');
+var automationInternal =
+    getInternalApi ?
+        getInternalApi('automationInternal') :
+        require('binding').Binding.create('automationInternal').generate();
+var exceptionHandler = require('uncaught_exception_handler');
+var logging = requireNative('logging');
+var nativeAutomationInternal = requireNative('automationInternal');
+var DestroyAccessibilityTree =
+    nativeAutomationInternal.DestroyAccessibilityTree;
+var GetIntAttribute = nativeAutomationInternal.GetIntAttribute;
+var StartCachingAccessibilityTrees =
+    nativeAutomationInternal.StartCachingAccessibilityTrees;
+var AddTreeChangeObserver = nativeAutomationInternal.AddTreeChangeObserver;
+var RemoveTreeChangeObserver =
+    nativeAutomationInternal.RemoveTreeChangeObserver;
+var GetFocusNative = nativeAutomationInternal.GetFocus;
+
+var jsLastError = bindingUtil ? undefined : require('lastError');
+function hasLastError() {
+  return bindingUtil ?
+      bindingUtil.hasLastError() : jsLastError.hasError(chrome);
+}
+
+/**
+ * A namespace to export utility functions to other files in automation.
+ */
+window.automationUtil = function() {};
+
+// TODO(aboxhall): Look into using WeakMap
+var idToCallback = {};
+
+var DESKTOP_TREE_ID = 0;
+
+automationUtil.storeTreeCallback = function(id, callback) {
+  if (!callback)
+    return;
+
+  var targetTree = AutomationRootNode.get(id);
+  if (!targetTree) {
+    // If we haven't cached the tree, hold the callback until the tree is
+    // populated by the initial onAccessibilityEvent call.
+    if (id in idToCallback)
+      idToCallback[id].push(callback);
+    else
+      idToCallback[id] = [callback];
+  } else {
+    callback(targetTree);
+  }
+};
+
+/**
+ * Global list of tree change observers.
+ * @type {Object<number, TreeChangeObserver>}
+ */
+automationUtil.treeChangeObserverMap = {};
+
+/**
+ * The id of the next tree change observer.
+ * @type {number}
+ */
+automationUtil.nextTreeChangeObserverId = 1;
+
+/**
+ * @type {AutomationNode} The current focused node. This is only updated
+ *   when calling automationUtil.updateFocusedNode.
+ */
+automationUtil.focusedNode = null;
+
+/**
+ * Gets the currently focused AutomationNode.
+ * @return {AutomationNode}
+ */
+automationUtil.getFocus = function() {
+  var focusedNodeInfo = GetFocusNative(DESKTOP_TREE_ID);
+  if (!focusedNodeInfo)
+    return null;
+  var tree = AutomationRootNode.getOrCreate(focusedNodeInfo.treeId);
+  if (tree)
+    return privates(tree).impl.get(focusedNodeInfo.nodeId);
+};
+
+/**
+ * Update automationUtil.focusedNode to be the node that currently has focus.
+ */
+automationUtil.updateFocusedNode = function() {
+  automationUtil.focusedNode = automationUtil.getFocus();
+};
+
+/**
+ * Updates the focus on blur.
+ */
+automationUtil.updateFocusedNodeOnBlur = function() {
+  var focus = automationUtil.getFocus();
+  automationUtil.focusedNode = focus ? focus.root : null;
+};
+
+automation.registerCustomHook(function(bindingsAPI) {
+  var apiFunctions = bindingsAPI.apiFunctions;
+
+  // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
+  apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) {
+    StartCachingAccessibilityTrees();
+
+    // enableTab() ensures the renderer for the active or specified tab has
+    // accessibility enabled, and fetches its ax tree id to use as
+    // a key in the idToAutomationRootNode map. The callback to
+    // enableTab is bound to the callback passed in to getTree(), so that once
+    // the tree is available (either due to having been cached earlier, or after
+    // an accessibility event occurs which causes the tree to be populated), the
+    // callback can be called.
+    var params = { tabID: tabID };
+    automationInternal.enableTab(params,
+        function onEnable(id) {
+          if (hasLastError()) {
+            callback();
+            return;
+          }
+          automationUtil.storeTreeCallback(id, callback);
+        });
+  });
+
+  var desktopTree = null;
+  apiFunctions.setHandleRequest('getDesktop', function(callback) {
+    StartCachingAccessibilityTrees();
+    desktopTree = AutomationRootNode.get(DESKTOP_TREE_ID);
+    if (!desktopTree) {
+      if (DESKTOP_TREE_ID in idToCallback)
+        idToCallback[DESKTOP_TREE_ID].push(callback);
+      else
+        idToCallback[DESKTOP_TREE_ID] = [callback];
+
+      // TODO(dtseng): Disable desktop tree once desktop object goes out of
+      // scope.
+      automationInternal.enableDesktop(function() {
+        if (hasLastError()) {
+          AutomationRootNode.destroy(DESKTOP_TREE_ID);
+          callback();
+          return;
+        }
+      });
+    } else {
+      callback(desktopTree);
+    }
+  });
+
+  apiFunctions.setHandleRequest('getFocus', function(callback) {
+    callback(automationUtil.getFocus());
+  });
+
+  function removeTreeChangeObserver(observer) {
+    for (var id in automationUtil.treeChangeObserverMap) {
+      if (automationUtil.treeChangeObserverMap[id] == observer) {
+        RemoveTreeChangeObserver(id);
+        delete automationUtil.treeChangeObserverMap[id];
+        return;
+      }
+    }
+  }
+  apiFunctions.setHandleRequest('removeTreeChangeObserver', function(observer) {
+    removeTreeChangeObserver(observer);
+  });
+
+  function addTreeChangeObserver(filter, observer) {
+    removeTreeChangeObserver(observer);
+    var id = automationUtil.nextTreeChangeObserverId++;
+    AddTreeChangeObserver(id, filter);
+    automationUtil.treeChangeObserverMap[id] = observer;
+  }
+  apiFunctions.setHandleRequest('addTreeChangeObserver',
+      function(filter, observer) {
+    addTreeChangeObserver(filter, observer);
+  });
+
+  apiFunctions.setHandleRequest('setDocumentSelection', function(params) {
+    var anchorNodeImpl = privates(params.anchorObject).impl;
+    var focusNodeImpl = privates(params.focusObject).impl;
+    if (anchorNodeImpl.treeID !== focusNodeImpl.treeID)
+      throw new Error('Selection anchor and focus must be in the same tree.');
+    if (anchorNodeImpl.treeID === DESKTOP_TREE_ID) {
+      throw new Error('Use AutomationNode.setSelection to set the selection ' +
+          'in the desktop tree.');
+    }
+    automationInternal.performAction({ treeID: anchorNodeImpl.treeID,
+                                       automationNodeID: anchorNodeImpl.id,
+                                       actionType: 'setSelection'},
+                                     { focusNodeID: focusNodeImpl.id,
+                                       anchorOffset: params.anchorOffset,
+                                       focusOffset: params.focusOffset });
+  });
+
+});
+
+automationInternal.onChildTreeID.addListener(function(treeID,
+                                                      nodeID) {
+  var tree = AutomationRootNode.getOrCreate(treeID);
+  if (!tree)
+    return;
+
+  var node = privates(tree).impl.get(nodeID);
+  if (!node)
+    return;
+
+  // A WebView in the desktop tree has a different AX tree as its child.
+  // When we encounter a WebView with a child AX tree id that we don't
+  // currently have cached, explicitly request that AX tree from the
+  // browser process and set up a callback when it loads to attach that
+  // tree as a child of this node and fire appropriate events.
+  var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId');
+  if (!childTreeID)
+    return;
+
+  var subroot = AutomationRootNode.get(childTreeID);
+  if (!subroot || subroot.role == 'unknown') {
+    automationUtil.storeTreeCallback(childTreeID, function(root) {
+      // Return early if the root has already been attached.
+      if (root.parent)
+        return;
+
+      privates(root).impl.setHostNode(node);
+
+      if (root.docLoaded) {
+        privates(root).impl.dispatchEvent('loadComplete', 'page');
+      }
+
+      privates(node).impl.dispatchEvent('childrenChanged', 'none');
+    });
+
+    automationInternal.enableFrame(childTreeID);
+  } else {
+    privates(subroot).impl.setHostNode(node);
+  }
+});
+
+automationInternal.onTreeChange.addListener(function(observerID,
+                                                     treeID,
+                                                     nodeID,
+                                                     changeType) {
+  var tree = AutomationRootNode.getOrCreate(treeID);
+  if (!tree)
+    return;
+
+  var node = privates(tree).impl.get(nodeID);
+  if (!node)
+    return;
+
+  var observer = automationUtil.treeChangeObserverMap[observerID];
+  if (!observer)
+    return;
+
+  try {
+    observer({target: node, type: changeType});
+  } catch (e) {
+    exceptionHandler.handle('Error in tree change observer for ' +
+        treeChange.type, e);
+  }
+});
+
+automationInternal.onNodesRemoved.addListener(function(treeID, nodeIDs) {
+  var tree = AutomationRootNode.getOrCreate(treeID);
+  if (!tree)
+    return;
+
+  for (var i = 0; i < nodeIDs.length; i++) {
+    privates(tree).impl.remove(nodeIDs[i]);
+  }
+});
+
+/**
+ * Dispatch accessibility events fired on individual nodes to its
+ * corresponding AutomationNode. Handle focus events specially
+ * (see below).
+ */
+automationInternal.onAccessibilityEvent.addListener(function(eventParams) {
+  var id = eventParams.treeID;
+  var targetTree = AutomationRootNode.getOrCreate(id);
+  if (eventParams.eventType == 'blur') {
+    // Work around an issue where Chrome sends us 'blur' events on the
+    // root node when nothing has focus, we need to treat those as focus
+    // events but otherwise not handle blur events specially.
+    var node = privates(targetTree).impl.get(eventParams.targetID);
+    if (node == node.root)
+      automationUtil.updateFocusedNodeOnBlur();
+  } else if (eventParams.eventType == 'mediaStartedPlaying' ||
+      eventParams.eventType == 'mediaStoppedPlaying') {
+    // These events are global to the tree.
+    eventParams.targetID = privates(targetTree).impl.id;
+  } else {
+    var previousFocusedNode = automationUtil.focusedNode;
+    automationUtil.updateFocusedNode();
+
+    // Fire focus events if necessary.
+    if (automationUtil.focusedNode &&
+        automationUtil.focusedNode != previousFocusedNode) {
+      var eventParamsCopy = {};
+      for (var key in eventParams)
+        eventParamsCopy[key] = eventParams[key];
+      eventParamsCopy['eventType'] = 'focus';
+      eventParamsCopy['treeID'] =
+          privates(automationUtil.focusedNode.root).impl.treeID;
+      eventParamsCopy['targetID'] =
+          privates(automationUtil.focusedNode).impl.id;
+      privates(automationUtil.focusedNode.root)
+          .impl.onAccessibilityEvent(eventParamsCopy);
+    }
+  }
+
+  // Note that focus type events have already been handled above if there was a
+  // focused node. All other events, even non-focus events that triggered a
+  // focus dispatch, still need to have their original event fired.
+  if ((!automationUtil.focusedNode || eventParams.eventType != 'focus') &&
+      !privates(targetTree).impl.onAccessibilityEvent(eventParams))
+    return;
+
+  // If we're not waiting on a callback to getTree(), we can early out here.
+  if (!(id in idToCallback))
+    return;
+
+  // We usually get a 'placeholder' tree first, which doesn't have any url
+  // attribute or child nodes. If we've got that, wait for the full tree before
+  // calling the callback.
+  // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
+  if (id != DESKTOP_TREE_ID && !targetTree.url &&
+      targetTree.children.length == 0)
+    return;
+
+  // If the tree wasn't available when getTree() was called, the callback will
+  // have been cached in idToCallback, so call and delete it now that we
+  // have the complete tree.
+  for (var i = 0; i < idToCallback[id].length; i++) {
+    var callback = idToCallback[id][i];
+    callback(targetTree);
+  }
+  delete idToCallback[id];
+});
+
+automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
+  // Destroy the AutomationRootNode.
+  var targetTree = AutomationRootNode.get(id);
+  if (targetTree) {
+    privates(targetTree).impl.destroy();
+    AutomationRootNode.destroy(id);
+  } else {
+    logging.WARNING('no targetTree to destroy');
+  }
+
+  // Destroy the native cache of the accessibility tree.
+  DestroyAccessibilityTree(id);
+});
+
+automationInternal.onAccessibilityTreeSerializationError.addListener(
+    function(id) {
+  automationInternal.enableFrame(id);
+});
+
+automationInternal.onActionResult.addListener(
+    function(treeID, requestID, result) {
+  var targetTree = AutomationRootNode.get(treeID);
+  if (!targetTree)
+    return;
+
+  privates(targetTree).impl.onActionResult(requestID, result);
+    });
+
+if (!apiBridge)
+  exports.$set('binding', automation.generate());
diff --git a/chromecast/renderer/resources/extensions_renderer_resources.grd b/chromecast/renderer/resources/extensions_renderer_resources.grd
new file mode 100644
index 0000000..17bc2fbd
--- /dev/null
+++ b/chromecast/renderer/resources/extensions_renderer_resources.grd
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/extensions_renderer_resources.h" type="rc_header" context="default">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="extensions_renderer_resources.pak" type="data_package" context="default" />
+  </outputs>
+  <release seq="1">
+    <includes>
+      <!-- Custom bindings for extension APIs. -->
+      <include name="IDR_AUTOMATION_CUSTOM_BINDINGS_JS" file="extensions\automation_custom_bindings.js" type="BINDATA" />
+      <include name="IDR_AUTOMATION_EVENT_JS" file="extensions\automation\automation_event.js" type="BINDATA" />
+      <include name="IDR_AUTOMATION_NODE_JS" file="extensions\automation\automation_node.js" type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 04e26a4e..b4f5914 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -7,7 +7,6 @@
 #include <string>
 
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/metrics/field_trial.h"
 #include "third_party/icu/source/common/unicode/locid.h"
 
@@ -28,10 +27,6 @@
 // all stored user keys will be converted to GaiaId)
 const char kTestCrosGaiaIdMigrationStarted[] = "started";
 
-// Controls whether enable Google Assistant feature.
-const base::Feature kAssistantFeature{"ChromeOSAssistant",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Controls whether enable assistant for locale.
 const base::Feature kAssistantFeatureForLocale{
     "ChromeOSAssistantForLocale", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -48,6 +43,10 @@
 
 }  // namespace
 
+// Controls whether enable Google Assistant feature.
+const base::Feature kAssistantFeature{"ChromeOSAssistant",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Please keep the order of these switches synchronized with the header file
 // (i.e. in alphabetical order).
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 01b07ed..c09dff7 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -5,6 +5,7 @@
 #ifndef CHROMEOS_CHROMEOS_SWITCHES_H_
 #define CHROMEOS_CHROMEOS_SWITCHES_H_
 
+#include "base/feature_list.h"
 #include "base/memory/memory_pressure_monitor_chromeos.h"
 #include "chromeos/chromeos_export.h"
 
@@ -159,6 +160,9 @@
 CHROMEOS_EXPORT extern const char kWaitForInitialPolicyFetchForTest[];
 CHROMEOS_EXPORT extern const char kWakeOnWifiPacket[];
 
+// Controls whether enable Google Assistant feature.
+CHROMEOS_EXPORT extern const base::Feature kAssistantFeature;
+
 // Returns true if the system should wake in response to wifi traffic.
 CHROMEOS_EXPORT bool WakeOnWifiEnabled();
 
diff --git a/chromeos/dbus/fake_smb_provider_client.cc b/chromeos/dbus/fake_smb_provider_client.cc
index 2f0e89b..7fd6f7924 100644
--- a/chromeos/dbus/fake_smb_provider_client.cc
+++ b/chromeos/dbus/fake_smb_provider_client.cc
@@ -158,4 +158,10 @@
       base::BindOnce(std::move(callback), smbprovider::ERROR_OK, entry_list));
 }
 
+void FakeSmbProviderClient::SetupKerberos(const std::string& account_id,
+                                          SetupKerberosCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), true /* success */));
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/fake_smb_provider_client.h b/chromeos/dbus/fake_smb_provider_client.h
index 537390a..9599440 100644
--- a/chromeos/dbus/fake_smb_provider_client.h
+++ b/chromeos/dbus/fake_smb_provider_client.h
@@ -91,6 +91,9 @@
   void GetShares(const base::FilePath& server_url,
                  ReadDirectoryCallback callback) override;
 
+  void SetupKerberos(const std::string& account_id,
+                     SetupKerberosCallback 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 f9a6cae..6ae80c8 100644
--- a/chromeos/dbus/smb_provider_client.cc
+++ b/chromeos/dbus/smb_provider_client.cc
@@ -259,6 +259,16 @@
                &callback);
   }
 
+  void SetupKerberos(const std::string& account_id,
+                     SetupKerberosCallback callback) override {
+    dbus::MethodCall method_call(smbprovider::kSmbProviderInterface,
+                                 smbprovider::kSetupKerberosMethod);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(account_id);
+    CallMethod(&method_call,
+               &SmbProviderClientImpl::HandleSetupKerberosCallback, &callback);
+  }
+
  protected:
   // DBusClient override.
   void Init(dbus::Bus* bus) override {
@@ -419,6 +429,19 @@
     std::move(callback).Run(smbprovider::ERROR_OK, delete_list);
   }
 
+  // Handles D-Bus callback for SetupKerberos.
+  void HandleSetupKerberosCallback(SetupKerberosCallback callback,
+                                   dbus::Response* response) {
+    dbus::MessageReader reader(response);
+    bool result;
+    if (!reader.PopBool(&result)) {
+      LOG(ERROR) << "SetupKerberos: parse failure.";
+      std::move(callback).Run(false /* success */);
+    }
+
+    std::move(callback).Run(result);
+  }
+
   // Default callback handler for D-Bus calls.
   void HandleDefaultCallback(const std::string& method_name,
                              StatusCallback callback,
diff --git a/chromeos/dbus/smb_provider_client.h b/chromeos/dbus/smb_provider_client.h
index c5c88bb..b28a042a 100644
--- a/chromeos/dbus/smb_provider_client.h
+++ b/chromeos/dbus/smb_provider_client.h
@@ -40,6 +40,7 @@
   using GetDeleteListCallback =
       base::OnceCallback<void(smbprovider::ErrorType error,
                               const smbprovider::DeleteListProto& delete_list)>;
+  using SetupKerberosCallback = base::OnceCallback<void(bool success)>;
 
   ~SmbProviderClient() override;
 
@@ -173,6 +174,12 @@
   virtual void GetShares(const base::FilePath& server_url,
                          ReadDirectoryCallback callback) = 0;
 
+  // Calls SetupKerberos. This sets up Kerberos for the user |account_id|,
+  // fetching the user's Kerberos files from AuthPolicy. The user must be
+  // ChromAD enrolled.
+  virtual void SetupKerberos(const std::string& account_id,
+                             SetupKerberosCallback callback) = 0;
+
  protected:
   // Create() should be used instead.
   SmbProviderClient();
diff --git a/chromeos/login/auth/stub_authenticator.cc b/chromeos/login/auth/stub_authenticator.cc
index cb619ab1..835251d4 100644
--- a/chromeos/login/auth/stub_authenticator.cc
+++ b/chromeos/login/auth/stub_authenticator.cc
@@ -79,7 +79,8 @@
 void StubAuthenticator::LoginAsKioskAccount(
     const AccountId& /* app_account_id */,
     bool use_guest_mount) {
-  UserContext user_context(expected_user_context_.GetAccountId());
+  UserContext user_context(user_manager::UserType::USER_TYPE_KIOSK_APP,
+                           expected_user_context_.GetAccountId());
   user_context.SetIsUsingOAuth(false);
   user_context.SetUserIDHash(
       expected_user_context_.GetAccountId().GetUserEmail() + kUserIdHashSuffix);
@@ -88,7 +89,8 @@
 
 void StubAuthenticator::LoginAsArcKioskAccount(
     const AccountId& /* app_account_id */) {
-  UserContext user_context(expected_user_context_.GetAccountId());
+  UserContext user_context(user_manager::USER_TYPE_ARC_KIOSK_APP,
+                           expected_user_context_.GetAccountId());
   user_context.SetIsUsingOAuth(false);
   user_context.SetUserIDHash(
       expected_user_context_.GetAccountId().GetUserEmail() + kUserIdHashSuffix);
diff --git a/chromeos/login/auth/user_context.cc b/chromeos/login/auth/user_context.cc
index 1e58b8c9..e4db1c96 100644
--- a/chromeos/login/auth/user_context.cc
+++ b/chromeos/login/auth/user_context.cc
@@ -3,35 +3,23 @@
 // found in the LICENSE file.
 
 #include "chromeos/login/auth/user_context.h"
+#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 
 namespace chromeos {
 
-namespace {
-
-user_manager::UserType CalculateUserType(const AccountId& account_id) {
-  // UserManager is not initialized in unit tests.
-  if (!user_manager::UserManager::IsInitialized())
-    return user_manager::USER_TYPE_REGULAR;
-
-  if (user_manager::UserManager::Get()->IsSupervisedAccountId(account_id))
-    return user_manager::USER_TYPE_SUPERVISED;
-
-  if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
-    return user_manager::USER_TYPE_ACTIVE_DIRECTORY;
-
-  return user_manager::USER_TYPE_REGULAR;
-}
-
-}  // anonymous namespace
-
 UserContext::UserContext() : account_id_(EmptyAccountId()) {}
 
 UserContext::UserContext(const UserContext& other) = default;
 
-UserContext::UserContext(const AccountId& account_id)
-    : UserContext(CalculateUserType(account_id), account_id) {}
+UserContext::UserContext(const user_manager::User& user)
+    : account_id_(user.GetAccountId()), user_type_(user.GetType()) {
+  if (user_type_ == user_manager::USER_TYPE_REGULAR) {
+    account_id_.SetUserEmail(
+        user_manager::CanonicalizeUserID(account_id_.GetUserEmail()));
+  }
+}
 
 UserContext::UserContext(user_manager::UserType user_type,
                          const AccountId& account_id)
diff --git a/chromeos/login/auth/user_context.h b/chromeos/login/auth/user_context.h
index 26d1f7e..6159022 100644
--- a/chromeos/login/auth/user_context.h
+++ b/chromeos/login/auth/user_context.h
@@ -17,6 +17,10 @@
 
 class AccountId;
 
+namespace user_manager {
+class User;
+}
+
 namespace chromeos {
 
 // Information that is passed around while authentication is in progress. The
@@ -42,7 +46,7 @@
 
   UserContext();
   UserContext(const UserContext& other);
-  explicit UserContext(const AccountId& account_id);
+  explicit UserContext(const user_manager::User& user);
   UserContext(user_manager::UserType user_type, const AccountId& account_id);
   ~UserContext();
 
diff --git a/components/app_modal_strings_grdp/OWNERS b/components/app_modal_strings_grdp/OWNERS
new file mode 100644
index 0000000..d092bde
--- /dev/null
+++ b/components/app_modal_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/app_modal/OWNERS
diff --git a/components/app_modal_strings_grdp/README.md b/components/app_modal_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/app_modal_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/assist_ranker/fake_ranker_model_loader.cc b/components/assist_ranker/fake_ranker_model_loader.cc
index b1804ce..9097243 100644
--- a/components/assist_ranker/fake_ranker_model_loader.cc
+++ b/components/assist_ranker/fake_ranker_model_loader.cc
@@ -19,7 +19,7 @@
 FakeRankerModelLoader::~FakeRankerModelLoader() {}
 
 void FakeRankerModelLoader::NotifyOfRankerActivity() {
-  if (validate_model_cb_.Run(*ranker_model_.get()) == RankerModelStatus::OK) {
+  if (validate_model_cb_.Run(*ranker_model_) == RankerModelStatus::OK) {
     on_model_available_cb_.Run(std::move(ranker_model_));
   }
 }
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index 1db77a6c..f5dabe4 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -131,7 +131,7 @@
 
 class FormAutofillUtilsTest : public content::RenderViewTest {
  public:
-  FormAutofillUtilsTest() : content::RenderViewTest() {}
+  FormAutofillUtilsTest() {}
   ~FormAutofillUtilsTest() override {}
 };
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index b163be5..0432318 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -662,7 +662,6 @@
     content::RenderFrame* render_frame,
     service_manager::BinderRegistry* registry)
     : content::RenderFrameObserver(render_frame),
-      web_input_to_password_info_(),
       last_supplied_password_info_iter_(web_input_to_password_info_.end()),
       logging_state_active_(false),
       was_username_autofilled_(false),
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index 8737792..30058360 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -191,7 +191,7 @@
 
 class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
  public:
-  MAYBE_PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
+  MAYBE_PasswordFormConversionUtilsTest() {}
   ~MAYBE_PasswordFormConversionUtilsTest() override {}
 
  protected:
diff --git a/components/autofill/core/browser/address.cc b/components/autofill/core/browser/address.cc
index 04f2d8c..f3ab3a3 100644
--- a/components/autofill/core/browser/address.cc
+++ b/components/autofill/core/browser/address.cc
@@ -27,7 +27,7 @@
 
 Address::Address() {}
 
-Address::Address(const Address& address) : FormGroup() {
+Address::Address(const Address& address) {
   *this = address;
 }
 
diff --git a/components/autofill/core/browser/autocomplete_history_manager.cc b/components/autofill/core/browser/autocomplete_history_manager.cc
index 1a4f54a..f9f06cf 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -68,7 +68,7 @@
     return;
   }
 
-  if (database_.get()) {
+  if (database_) {
     pending_query_handle_ = database_->GetFormValuesForElementName(
         name, prefix, kMaxAutocompleteMenuItems, this);
   }
@@ -108,7 +108,7 @@
 
 void AutocompleteHistoryManager::OnRemoveAutocompleteEntry(
     const base::string16& name, const base::string16& value) {
-  if (database_.get())
+  if (database_)
     database_->RemoveFormValueForElementName(name, value);
 }
 
@@ -119,7 +119,7 @@
 
 void AutocompleteHistoryManager::CancelPendingQuery() {
   if (pending_query_handle_) {
-    if (database_.get())
+    if (database_)
       database_->CancelRequest(pending_query_handle_);
     pending_query_handle_ = 0;
   }
diff --git a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
index 369d298..c22cf0c4 100644
--- a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
+++ b/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
@@ -101,7 +101,7 @@
   valid_cc.form_control_type = "text";
   form.fields.push_back(valid_cc);
 
-  EXPECT_CALL(*web_data_service_.get(), AddFormFields(_)).Times(0);
+  EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
   autocomplete_manager_->OnWillSubmitForm(form);
 }
 
@@ -140,7 +140,7 @@
   ssn.form_control_type = "text";
   form.fields.push_back(ssn);
 
-  EXPECT_CALL(*web_data_service_.get(), AddFormFields(_)).Times(0);
+  EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
   autocomplete_manager_->OnWillSubmitForm(form);
 }
 
@@ -182,7 +182,7 @@
   field.should_autocomplete = false;
   form.fields.push_back(field);
 
-  EXPECT_CALL(*web_data_service_.get(), AddFormFields(_)).Times(0);
+  EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
   autocomplete_manager_->OnWillSubmitForm(form);
 }
 
diff --git a/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc b/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc
index 15ad630..1edd5bd 100644
--- a/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc
+++ b/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc
@@ -79,7 +79,7 @@
                                    const wchar_t* value_name) {
   DWORD data_type = REG_BINARY;
   DWORD data_size = 0;
-  LONG result = key.ReadValue(value_name, NULL, &data_size, &data_type);
+  LONG result = key.ReadValue(value_name, nullptr, &data_size, &data_type);
   if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY)
     return base::string16();
   std::string data;
@@ -284,8 +284,8 @@
       RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
       CreditCard credit_card;
       credit_card.set_origin(kIEToolbarImportOrigin);
-      if (ImportSingleFormGroup(
-              key, reg_to_field, app_locale, &credit_card, NULL)) {
+      if (ImportSingleFormGroup(key, reg_to_field, app_locale, &credit_card,
+                                nullptr)) {
         base::string16 cc_number = credit_card.GetRawInfo(CREDIT_CARD_NUMBER);
         if (!cc_number.empty())
           credit_cards->push_back(credit_card);
diff --git a/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc b/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
index aba32f1..abc0088 100644
--- a/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
+++ b/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
@@ -141,7 +141,7 @@
 }
 
 void AutofillIeToolbarImportTest::TearDown() {
-  EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
+  EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, nullptr));
   temp_hkcu_hive_key_.Close();
   RegKey key(HKEY_CURRENT_USER, kUnitTestRegistrySubKey, KEY_ALL_ACCESS);
   key.DeleteKey(L"");
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index dc7f14a..b60bec26 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -108,11 +108,11 @@
 }
 
 MATCHER(CompareMetricsIgnoringMillisecondsSinceFormParsed, "") {
-  const ukm::mojom::UkmMetric* lhs = ::testing::get<0>(arg).get();
+  const auto& lhs = ::testing::get<0>(arg);
   const std::pair<const char*, int64_t>& rhs = ::testing::get<1>(arg);
-  return lhs->metric_hash == base::HashMetricName(rhs.first) &&
-         (lhs->value == rhs.second ||
-          (lhs->value > 0 &&
+  return lhs.first == base::HashMetricName(rhs.first) &&
+         (lhs.second == rhs.second ||
+          (lhs.second > 0 &&
            rhs.first ==
                UkmSuggestionFilledType::kMillisecondsSinceFormParsedName));
 }
diff --git a/components/autofill/core/browser/contact_info.cc b/components/autofill/core/browser/contact_info.cc
index 7d54216..105d135c 100644
--- a/components/autofill/core/browser/contact_info.cc
+++ b/components/autofill/core/browser/contact_info.cc
@@ -20,7 +20,7 @@
 
 NameInfo::NameInfo() {}
 
-NameInfo::NameInfo(const NameInfo& info) : FormGroup() {
+NameInfo::NameInfo(const NameInfo& info) {
   *this = info;
 }
 
@@ -181,7 +181,7 @@
 
 EmailInfo::EmailInfo() {}
 
-EmailInfo::EmailInfo(const EmailInfo& info) : FormGroup() {
+EmailInfo::EmailInfo(const EmailInfo& info) {
   *this = info;
 }
 
@@ -217,7 +217,7 @@
 
 CompanyInfo::CompanyInfo() {}
 
-CompanyInfo::CompanyInfo(const CompanyInfo& info) : FormGroup() {
+CompanyInfo::CompanyInfo(const CompanyInfo& info) {
   *this = info;
 }
 
diff --git a/components/autofill/core/browser/country_combobox_model.cc b/components/autofill/core/browser/country_combobox_model.cc
index 5fe8095..280c0234 100644
--- a/components/autofill/core/browser/country_combobox_model.cc
+++ b/components/autofill/core/browser/country_combobox_model.cc
@@ -89,7 +89,7 @@
 }
 
 bool CountryComboboxModel::IsItemSeparatorAt(int index) {
-  return !countries_[index].get();
+  return !countries_[index];
 }
 
 std::string CountryComboboxModel::GetDefaultCountryCode() const {
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 4809b10..a012ab2 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -300,7 +300,7 @@
     const FormStructure& form,
     bool should_return_local_card,
     std::unique_ptr<CreditCard>* imported_credit_card) {
-  DCHECK(!imported_credit_card->get());
+  DCHECK(!*imported_credit_card);
 
   // The candidate for credit card import. There are many ways for the candidate
   // to be rejected (see everywhere this function returns false, below).
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 23c69b4..43fdcc0 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -378,7 +378,7 @@
     AutofillMetrics::LogIsAutofillEnabledAtStartup(IsAutofillEnabled());
 
   // WebDataService may not be available in tests.
-  if (!database_.get())
+  if (!database_)
     return;
 
   LoadProfiles();
@@ -402,7 +402,7 @@
   CancelPendingQuery(&pending_creditcards_query_);
   CancelPendingQuery(&pending_server_creditcards_query_);
 
-  if (database_.get())
+  if (database_)
     database_->RemoveObserver(this);
 }
 
@@ -548,7 +548,7 @@
 }
 
 void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
-  if (is_off_the_record_ || !database_.get())
+  if (is_off_the_record_ || !database_)
     return;
 
   CreditCard* credit_card = GetCreditCardByGUID(data_model.guid());
@@ -588,7 +588,7 @@
   if (FindByGUID<AutofillProfile>(web_profiles_, profile.guid()))
     return;
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Don't add a duplicate.
@@ -619,7 +619,7 @@
     return;
   }
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Make the update.
@@ -653,7 +653,7 @@
   if (FindByGUID<CreditCard>(local_credit_cards_, credit_card.guid()))
     return;
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Don't add a duplicate.
@@ -688,7 +688,7 @@
   // Update the cached version.
   *existing_credit_card = credit_card;
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Make the update.
@@ -704,7 +704,7 @@
   DCHECK(!credit_card.IsEmpty(app_locale_));
   DCHECK(!credit_card.server_id().empty());
 
-  if (is_off_the_record_ || !database_.get())
+  if (is_off_the_record_ || !database_)
     return;
 
   // Don't add a duplicate.
@@ -723,7 +723,7 @@
     const CreditCard& credit_card) {
   DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
 
-  if (is_off_the_record_ || !database_.get())
+  if (is_off_the_record_ || !database_)
     return;
 
   // Look up by server id, not GUID.
@@ -752,7 +752,7 @@
     const CreditCard& credit_card) {
   DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
 
-  if (is_off_the_record_ || !database_.get())
+  if (is_off_the_record_ || !database_)
     return;
 
   database_->UpdateServerCardMetadata(credit_card);
@@ -842,7 +842,7 @@
   if (!is_credit_card && !is_profile)
     return;
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   if (is_credit_card) {
@@ -1348,7 +1348,7 @@
                                  IsEmptyFunctor<AutofillProfile>(app_locale_)),
                   profiles->end());
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Any profiles that are not in the new profile list should be removed from
@@ -1391,7 +1391,7 @@
                                      IsEmptyFunctor<CreditCard>(app_locale_)),
                       credit_cards->end());
 
-  if (!database_.get())
+  if (!database_)
     return;
 
   // Any credit cards that are not in the new credit card list should be
@@ -1424,7 +1424,7 @@
 }
 
 void PersonalDataManager::LoadProfiles() {
-  if (!database_.get()) {
+  if (!database_) {
     NOTREACHED();
     return;
   }
@@ -1437,7 +1437,7 @@
 }
 
 void PersonalDataManager::LoadCreditCards() {
-  if (!database_.get()) {
+  if (!database_) {
     NOTREACHED();
     return;
   }
@@ -1452,7 +1452,7 @@
 void PersonalDataManager::CancelPendingQuery(
     WebDataServiceBase::Handle* handle) {
   if (*handle) {
-    if (!database_.get()) {
+    if (!database_) {
       NOTREACHED();
       return;
     }
diff --git a/components/autofill/core/browser/phone_number_i18n.cc b/components/autofill/core/browser/phone_number_i18n.cc
index 6fdd966..e849e44 100644
--- a/components/autofill/core/browser/phone_number_i18n.cc
+++ b/components/autofill/core/browser/phone_number_i18n.cc
@@ -241,14 +241,14 @@
 
   // Parse phone numbers based on the region
   ::i18n::phonenumbers::PhoneNumber i18n_number1;
-  if (phone_util->Parse(base::UTF16ToUTF8(number_a), region.c_str(),
-                        &i18n_number1) != PhoneNumberUtil::NO_PARSING_ERROR) {
+  if (phone_util->Parse(base::UTF16ToUTF8(number_a), region, &i18n_number1) !=
+      PhoneNumberUtil::NO_PARSING_ERROR) {
     return false;
   }
 
   ::i18n::phonenumbers::PhoneNumber i18n_number2;
-  if (phone_util->Parse(base::UTF16ToUTF8(number_b), region.c_str(),
-                        &i18n_number2) != PhoneNumberUtil::NO_PARSING_ERROR) {
+  if (phone_util->Parse(base::UTF16ToUTF8(number_b), region, &i18n_number2) !=
+      PhoneNumberUtil::NO_PARSING_ERROR) {
     return false;
   }
 
@@ -379,7 +379,7 @@
 
   region_ = other.region_;
 
-  if (other.i18n_number_.get())
+  if (other.i18n_number_)
     i18n_number_.reset(
         new ::i18n::phonenumbers::PhoneNumber(*other.i18n_number_));
   else
diff --git a/components/autofill/core/browser/region_data_loader_impl.cc b/components/autofill/core/browser/region_data_loader_impl.cc
index 4dbe7a91..9415cc2 100644
--- a/components/autofill/core/browser/region_data_loader_impl.cc
+++ b/components/autofill/core/browser/region_data_loader_impl.cc
@@ -31,7 +31,7 @@
     int64_t timeout_ms) {
   callback_ = callback;
   region_data_supplier_.LoadRules(country_code,
-                                  *region_data_supplier_callback_.get());
+                                  *region_data_supplier_callback_);
 
   timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_ms),
                base::Bind(&RegionDataLoaderImpl::OnRegionDataLoaded,
diff --git a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
index 08aad8eb9..349a5b9 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
@@ -95,9 +95,9 @@
     std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
     std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!sync_processor_.get());
-  DCHECK(sync_processor.get());
-  DCHECK(sync_error_factory.get());
+  DCHECK(!sync_processor_);
+  DCHECK(sync_processor);
+  DCHECK(sync_error_factory);
   DVLOG(1) << "Associating Autofill: MergeDataAndStartSyncing";
 
   syncer::SyncMergeResult merge_result(type);
@@ -216,7 +216,7 @@
 syncer::SyncDataList AutofillProfileSyncableService::GetAllSyncData(
     syncer::ModelType type) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(sync_processor_.get());
+  DCHECK(sync_processor_);
   DCHECK_EQ(type, syncer::AUTOFILL_PROFILE);
 
   syncer::SyncDataList current_data;
@@ -229,7 +229,7 @@
     const base::Location& from_here,
     const syncer::SyncChangeList& change_list) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!sync_processor_.get()) {
+  if (!sync_processor_) {
     syncer::SyncError error(FROM_HERE,
                             syncer::SyncError::DATATYPE_ERROR,
                             "Models not yet associated.",
@@ -279,7 +279,7 @@
   // up we are going to process all when MergeData..() is called. If we receive
   // notification after the sync exited, it will be sinced next time Chrome
   // starts.
-  if (sync_processor_.get()) {
+  if (sync_processor_) {
     ActOnChange(change);
   } else if (!flare_.is_null()) {
     flare_.Run(syncer::AUTOFILL_PROFILE);
@@ -579,7 +579,7 @@
       (change.type() == AutofillProfileChange::REMOVE &&
        !change.data_model()) ||
       (change.type() != AutofillProfileChange::REMOVE && change.data_model()));
-  DCHECK(sync_processor_.get());
+  DCHECK(sync_processor_);
 
   if (change.data_model() &&
       change.data_model()->record_type() != AutofillProfile::LOCAL_PROFILE) {
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index 27f562f9..d48dd08 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -49,7 +49,7 @@
 }
 
 AutofillWebDataBackendImpl::~AutofillWebDataBackendImpl() {
-  DCHECK(!user_data_.get());  // Forgot to call ResetUserData?
+  DCHECK(!user_data_);  // Forgot to call ResetUserData?
 }
 
 WebDatabase* AutofillWebDataBackendImpl::GetDatabase() {
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
index f81b0b5f..c9d3d8a5 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -261,14 +261,14 @@
 void AutofillWebDataService::AddObserver(
     AutofillWebDataServiceObserverOnDBSequence* observer) {
   DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  if (autofill_backend_.get())
+  if (autofill_backend_)
     autofill_backend_->AddObserver(observer);
 }
 
 void AutofillWebDataService::RemoveObserver(
     AutofillWebDataServiceObserverOnDBSequence* observer) {
   DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-  if (autofill_backend_.get())
+  if (autofill_backend_)
     autofill_backend_->RemoveObserver(observer);
 }
 
diff --git a/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
index 1afac62..528064c 100644
--- a/components/autofill/core/browser/webdata/web_data_service_unittest.cc
+++ b/components/autofill/core/browser/webdata/web_data_service_unittest.cc
@@ -133,8 +133,7 @@
 class WebDataServiceAutofillTest : public WebDataServiceTest {
  public:
   WebDataServiceAutofillTest()
-      : WebDataServiceTest(),
-        unique_id1_(1),
+      : unique_id1_(1),
         unique_id2_(2),
         test_timeout_(TimeDelta::FromSeconds(kWebDataServiceTimeoutSeconds)),
         done_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
diff --git a/components/autofill_strings_grdp/OWNERS b/components/autofill_strings_grdp/OWNERS
new file mode 100644
index 0000000..50a21bb
--- /dev/null
+++ b/components/autofill_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/OWNERS
diff --git a/components/autofill_strings_grdp/README.md b/components/autofill_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/autofill_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/bookmark_bar_strings_grdp/OWNERS b/components/bookmark_bar_strings_grdp/OWNERS
new file mode 100644
index 0000000..166b0f10
--- /dev/null
+++ b/components/bookmark_bar_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/bookmarks/OWNERS
diff --git a/components/bookmark_bar_strings_grdp/README.md b/components/bookmark_bar_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/bookmark_bar_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 58d9823..147ea6e 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -148,7 +148,7 @@
   for (BookmarkModelObserver& observer : observers_)
     observer.BookmarkModelBeingDeleted(this);
 
-  if (store_.get()) {
+  if (store_) {
     // The store maintains a reference back to us. We need to tell it we're gone
     // so that it doesn't try and invoke a method back on us again.
     store_->BookmarkModelDeleted();
@@ -261,7 +261,7 @@
     }
   }
   EndExtensiveChanges();
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -311,7 +311,7 @@
   BookmarkNode* mutable_new_parent = AsMutable(new_parent);
   mutable_new_parent->Add(std::move(owned_node), index);
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -339,7 +339,7 @@
   // don't need to send notifications here.
   CloneBookmarkNode(this, drag_data.elements, new_parent, index, true);
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 }
 
@@ -383,7 +383,7 @@
   if (node->is_url())
     index_->Add(node);
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -410,7 +410,7 @@
     AddNodeToInternalMaps(mutable_node);
   }
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -446,7 +446,7 @@
     observer.OnWillChangeBookmarkMetaInfo(this, node);
 
   AsMutable(node)->SetMetaInfoMap(meta_info_map);
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -482,7 +482,7 @@
     return;
 
   AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 }
 
@@ -531,7 +531,7 @@
   if (date_added > node->parent()->date_folder_modified()) {
     // Will trigger store_->ScheduleSave().
     SetDateFolderModified(node->parent(), date_added);
-  } else if (store_.get()) {
+  } else if (store_) {
     store_->ScheduleSave();
   }
 }
@@ -682,7 +682,7 @@
             mutable_parent->children().end(),
             SortComparator(collator.get()));
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -716,7 +716,7 @@
     }
     mutable_parent->children().swap(new_children);
 
-    if (store_.get())
+    if (store_)
       store_->ScheduleSave();
   }
 
@@ -729,7 +729,7 @@
   DCHECK(parent);
   AsMutable(parent)->set_date_folder_modified(time);
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 }
 
@@ -888,7 +888,7 @@
     node = RemoveNodeAndGetRemovedUrls(node_ptr, &removed_urls);
   }
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   for (BookmarkModelObserver& observer : observers_)
@@ -946,7 +946,7 @@
   BookmarkNode* node_ptr = node.get();
   parent->Add(std::move(node), index);
 
-  if (store_.get())
+  if (store_)
     store_->ScheduleSave();
 
   {
diff --git a/components/bookmarks/browser/bookmark_storage.cc b/components/bookmarks/browser/bookmark_storage.cc
index d1af975..fb493a9 100644
--- a/components/bookmarks/browser/bookmark_storage.cc
+++ b/components/bookmarks/browser/bookmark_storage.cc
@@ -69,14 +69,14 @@
     std::unique_ptr<base::Value> root =
         deserializer.Deserialize(nullptr, nullptr);
 
-    if (root.get()) {
+    if (root) {
       // Building the index can take a while, so we do it on the background
       // thread.
       int64_t max_node_id = 0;
       BookmarkCodec codec;
       TimeTicks start_time = TimeTicks::Now();
       codec.Decode(details->bb_node(), details->other_folder_node(),
-                   details->mobile_folder_node(), &max_node_id, *root.get());
+                   details->mobile_folder_node(), &max_node_id, *root);
       details->set_max_id(std::max(max_node_id, details->max_id()));
       details->set_computed_checksum(codec.computed_checksum());
       details->set_stored_checksum(codec.stored_checksum());
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index f7bf4c7..f70196f 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -945,7 +945,7 @@
   ClearUnrecoverableError();
   last_actionable_error_ = SyncProtocolError();
   // Clear the data type errors as well.
-  if (data_type_manager_.get())
+  if (data_type_manager_)
     data_type_manager_->ResetDataTypeErrors();
 }
 
diff --git a/components/browser_watcher/endsession_watcher_window_win.cc b/components/browser_watcher/endsession_watcher_window_win.cc
index cf4bb82..8f6d313 100644
--- a/components/browser_watcher/endsession_watcher_window_win.cc
+++ b/components/browser_watcher/endsession_watcher_window_win.cc
@@ -21,16 +21,15 @@
   WNDCLASSEX window_class = {0};
   base::win::InitializeWindowClass(
       kWindowClassName,
-      &base::win::WrappedWindowProc<EndSessionWatcherWindow::WndProcThunk>,
-      0, 0, 0, NULL, NULL, NULL, NULL, NULL,
-      &window_class);
+      &base::win::WrappedWindowProc<EndSessionWatcherWindow::WndProcThunk>, 0,
+      0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, &window_class);
   instance_ = window_class.hInstance;
   ATOM clazz = ::RegisterClassEx(&window_class);
   DCHECK(clazz);
 
   // TODO(siggi): will a message window do here?
-  window_ = ::CreateWindow(kWindowClassName,
-                           0, 0, 0, 0, 0, 0, 0, 0, instance_, 0);
+  window_ = ::CreateWindow(kWindowClassName, nullptr, 0, 0, 0, 0, 0, nullptr,
+                           nullptr, instance_, nullptr);
   ::SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
   DCHECK_EQ(::GetWindowLongPtr(window_, GWLP_USERDATA),
             reinterpret_cast<LONG_PTR>(this));
diff --git a/components/browser_watcher/exit_code_watcher_win_unittest.cc b/components/browser_watcher/exit_code_watcher_win_unittest.cc
index 9c1a074..e5edaa28 100644
--- a/components/browser_watcher/exit_code_watcher_win_unittest.cc
+++ b/components/browser_watcher/exit_code_watcher_win_unittest.cc
@@ -123,7 +123,7 @@
   ExitCodeWatcher watcher(kRegistryPath);
 
   // A waitable event has a non process-handle.
-  base::Process event(::CreateEvent(NULL, false, false, NULL));
+  base::Process event(::CreateEvent(nullptr, false, false, nullptr));
 
   // A non-process handle should fail.
   EXPECT_FALSE(watcher.Initialize(std::move(event)));
diff --git a/components/browser_watcher/watcher_metrics_provider_win_unittest.cc b/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
index af78d5d..65ba549a 100644
--- a/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
+++ b/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
@@ -84,7 +84,7 @@
                                      base::FilePath(),
                                      GetExecutableDetailsCallback());
 
-  provider.ProvideStabilityMetrics(NULL);
+  provider.ProvideStabilityMetrics(nullptr);
   histogram_tester_.ExpectBucketCount(
         WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 0, 11);
   histogram_tester_.ExpectBucketCount(
@@ -108,7 +108,7 @@
                                      base::FilePath(),
                                      GetExecutableDetailsCallback());
 
-  provider.ProvideStabilityMetrics(NULL);
+  provider.ProvideStabilityMetrics(nullptr);
   histogram_tester_.ExpectUniqueSample(
         WatcherMetricsProviderWin::kBrowserExitCodeHistogramName, 0, 11);
 
diff --git a/components/browser_watcher/window_hang_monitor_win.cc b/components/browser_watcher/window_hang_monitor_win.cc
index 96cadb62..ca89e6a 100644
--- a/components/browser_watcher/window_hang_monitor_win.cc
+++ b/components/browser_watcher/window_hang_monitor_win.cc
@@ -151,8 +151,8 @@
     // The ping is still outstanding, the window is hung or has vanished.
     // Orphan the outstanding ping. If the callback arrives late, it will
     // delete it, or if the callback never arrives it'll leak.
-    outstanding_ping_->monitor = NULL;
-    outstanding_ping_ = NULL;
+    outstanding_ping_->monitor = nullptr;
+    outstanding_ping_ = nullptr;
 
     if (hwnd != FindChromeMessageWindow(window_process_.Pid())) {
       // The window vanished.
diff --git a/components/browser_watcher/window_hang_monitor_win_unittest.cc b/components/browser_watcher/window_hang_monitor_win_unittest.cc
index da0baa81f..0ccf8a7 100644
--- a/components/browser_watcher/window_hang_monitor_win_unittest.cc
+++ b/components/browser_watcher/window_hang_monitor_win_unittest.cc
@@ -193,7 +193,7 @@
     *success = message_window_->CreateNamed(
         base::Bind(&MonitoredProcessClient::EmptyMessageCallback,
                    base::Unretained(this)),
-        existing_dir.value().c_str());
+        existing_dir.value());
     created->Signal();
   }
 
@@ -251,7 +251,7 @@
         thread_("Hang monitor thread") {}
 
   ~HangMonitorThread() {
-    if (hang_monitor_.get())
+    if (hang_monitor_)
       DestroyWatcher();
   }
 
diff --git a/components/browsing_data/content/conditional_cache_counting_helper.cc b/components/browsing_data/content/conditional_cache_counting_helper.cc
index 3e00187..b9b360b 100644
--- a/components/browsing_data/content/conditional_cache_counting_helper.cc
+++ b/components/browsing_data/content/conditional_cache_counting_helper.cc
@@ -82,8 +82,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   next_cache_state_ = CacheState::NONE;
   DCHECK_EQ(CacheState::NONE, next_cache_state_);
-  DCHECK(main_context_getter_.get());
-  DCHECK(media_context_getter_.get());
+  DCHECK(main_context_getter_);
+  DCHECK(media_context_getter_);
 
   next_cache_state_ = CacheState::CREATE_MAIN;
   DoCountCache(net::OK);
diff --git a/components/browsing_data_strings_grdp/OWNERS b/components/browsing_data_strings_grdp/OWNERS
new file mode 100644
index 0000000..93c7250
--- /dev/null
+++ b/components/browsing_data_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/browsing_data/OWNERS
diff --git a/components/browsing_data_strings_grdp/README.md b/components/browsing_data_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/browsing_data_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/captive_portal/captive_portal_detector.cc b/components/captive_portal/captive_portal_detector.cc
index b9397c0..90a39046 100644
--- a/components/captive_portal/captive_portal_detector.cc
+++ b/components/captive_portal/captive_portal_detector.cc
@@ -159,7 +159,7 @@
 }
 
 bool CaptivePortalDetector::FetchingURL() const {
-  return simple_loader_.get() != nullptr;
+  return simple_loader_ != nullptr;
 }
 
 }  // namespace captive_portal
diff --git a/components/cast_channel/cast_auth_util_unittest.cc b/components/cast_channel/cast_auth_util_unittest.cc
index d1493f2..5a9df90 100644
--- a/components/cast_channel/cast_auth_util_unittest.cc
+++ b/components/cast_channel/cast_auth_util_unittest.cc
@@ -174,7 +174,7 @@
   scoped_feature_list.InitAndEnableFeature(
       base::Feature{"CastNonceEnforced", base::FEATURE_DISABLED_BY_DEFAULT});
   AuthContext context = AuthContext::Create();
-  std::string received_nonce = "";
+  std::string received_nonce;
   EXPECT_FALSE(context.nonce().empty());
   AuthResult result = context.VerifySenderNonce(received_nonce);
   EXPECT_FALSE(result.success());
diff --git a/components/cast_channel/cast_socket.cc b/components/cast_channel/cast_socket.cc
index 01ff7ed4a1..5ef5fb1 100644
--- a/components/cast_channel/cast_socket.cc
+++ b/components/cast_channel/cast_socket.cc
@@ -168,7 +168,7 @@
   cert_verifier_ = base::WrapUnique(new FakeCertVerifier);
   transport_security_state_.reset(new net::TransportSecurityState);
   cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
-  ct_policy_enforcer_.reset(new net::CTPolicyEnforcer());
+  ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer());
 
   // Note that |context| fields remain owned by CastSocketImpl.
   net::SSLClientSocketContext context;
@@ -189,7 +189,7 @@
 
 scoped_refptr<net::X509Certificate> CastSocketImpl::ExtractPeerCert() {
   net::SSLInfo ssl_info;
-  if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert.get())
+  if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert)
     return nullptr;
 
   return ssl_info.cert;
@@ -430,7 +430,7 @@
     }
 
     // SSL connection succeeded.
-    if (!transport_.get()) {
+    if (!transport_) {
       // Create a channel transport if one wasn't already set (e.g. by test
       // code).
       transport_.reset(new CastTransportImpl(
diff --git a/components/cast_channel/cast_socket_unittest.cc b/components/cast_channel/cast_socket_unittest.cc
index eedc797..75c4635 100644
--- a/components/cast_channel/cast_socket_unittest.cc
+++ b/components/cast_channel/cast_socket_unittest.cc
@@ -357,7 +357,7 @@
   MockCastSocketTest() {}
 
   void TearDown() override {
-    if (socket_.get()) {
+    if (socket_) {
       EXPECT_CALL(handler_, OnCloseComplete(net::OK));
       socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
                                 base::Unretained(&handler_)));
@@ -396,7 +396,7 @@
   SslCastSocketTest() {}
 
   void TearDown() override {
-    if (socket_.get()) {
+    if (socket_) {
       EXPECT_CALL(handler_, OnCloseComplete(net::OK));
       socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
                                 base::Unretained(&handler_)));
diff --git a/components/cast_channel/cast_test_util.cc b/components/cast_channel/cast_test_util.cc
index f90d13d..d7f2796 100644
--- a/components/cast_channel/cast_test_util.cc
+++ b/components/cast_channel/cast_test_util.cc
@@ -30,8 +30,7 @@
 MockCastSocketObserver::~MockCastSocketObserver() {}
 
 MockCastSocketService::MockCastSocketService(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : CastSocketService() {
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   SetTaskRunnerForTest(task_runner);
 }
 MockCastSocketService::~MockCastSocketService() {}
diff --git a/components/certificate_transparency/BUILD.gn b/components/certificate_transparency/BUILD.gn
index a2dae9e..1e71b52 100644
--- a/components/certificate_transparency/BUILD.gn
+++ b/components/certificate_transparency/BUILD.gn
@@ -4,6 +4,8 @@
 
 static_library("certificate_transparency") {
   sources = [
+    "chrome_ct_policy_enforcer.cc",
+    "chrome_ct_policy_enforcer.h",
     "ct_policy_manager.cc",
     "ct_policy_manager.h",
     "features.cc",
@@ -36,6 +38,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "chrome_ct_policy_enforcer_unittest.cc",
     "ct_policy_manager_unittest.cc",
     "log_dns_client_unittest.cc",
     "mock_log_dns_traffic.cc",
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
new file mode 100644
index 0000000..d09c1d3
--- /dev/null
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -0,0 +1,307 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/certificate_transparency/chrome_ct_policy_enforcer.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/build_time.h"
+#include "base/callback_helpers.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "net/cert/ct_known_logs.h"
+#include "net/cert/ct_policy_status.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_certificate_net_log_param.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_parameters_callback.h"
+#include "net/log/net_log_with_source.h"
+
+using net::ct::CTPolicyCompliance;
+
+namespace certificate_transparency {
+
+namespace {
+
+// Returns true if the current build is recent enough to ensure that
+// built-in security information (e.g. CT Logs) is fresh enough.
+// TODO(eranm): Move to base or net/base
+bool IsBuildTimely() {
+  const base::Time build_time = base::GetBuildTime();
+  // We consider built-in information to be timely for 10 weeks.
+  return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
+}
+
+// Returns a rounded-down months difference of |start| and |end|,
+// together with an indication of whether the last month was
+// a full month, because the range starts specified in the policy
+// are not consistent in terms of including the range start value.
+void RoundedDownMonthDifference(const base::Time& start,
+                                const base::Time& end,
+                                size_t* rounded_months_difference,
+                                bool* has_partial_month) {
+  DCHECK(rounded_months_difference);
+  DCHECK(has_partial_month);
+  base::Time::Exploded exploded_start;
+  base::Time::Exploded exploded_expiry;
+  start.UTCExplode(&exploded_start);
+  end.UTCExplode(&exploded_expiry);
+  if (end < start) {
+    *rounded_months_difference = 0;
+    *has_partial_month = false;
+    return;
+  }
+
+  *has_partial_month = true;
+  uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
+                        (exploded_expiry.month - exploded_start.month);
+  if (exploded_expiry.day_of_month < exploded_start.day_of_month)
+    --month_diff;
+  else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
+    *has_partial_month = false;
+
+  *rounded_months_difference = month_diff;
+}
+
+const char* CTPolicyComplianceToString(CTPolicyCompliance status) {
+  switch (status) {
+    case CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
+      return "COMPLIES_VIA_SCTS";
+    case CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
+      return "NOT_ENOUGH_SCTS";
+    case CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
+      return "NOT_DIVERSE_SCTS";
+    case CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
+      return "BUILD_NOT_TIMELY";
+    case CTPolicyCompliance::CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
+    case CTPolicyCompliance::CT_POLICY_MAX:
+      NOTREACHED();
+      return "unknown";
+  }
+
+  NOTREACHED();
+  return "unknown";
+}
+
+std::unique_ptr<base::Value> NetLogCertComplianceCheckResultCallback(
+    net::X509Certificate* cert,
+    bool build_timely,
+    CTPolicyCompliance compliance,
+    net::NetLogCaptureMode capture_mode) {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  dict->Set("certificate",
+            net::NetLogX509CertificateCallback(cert, capture_mode));
+  dict->SetBoolean("build_timely", build_timely);
+  dict->SetString("ct_compliance_status",
+                  CTPolicyComplianceToString(compliance));
+  return std::move(dict);
+}
+
+// Evaluates against the policy specified at
+// https://sites.google.com/a/chromium.org/dev/Home/chromium-security/root-ca-policy/EVCTPlanMay2015edition.pdf?attredirects=0
+CTPolicyCompliance CheckCTPolicyCompliance(
+    const net::X509Certificate& cert,
+    const net::ct::SCTList& verified_scts) {
+  // Cert is outside the bounds of parsable; reject it.
+  if (cert.valid_start().is_null() || cert.valid_expiry().is_null() ||
+      cert.valid_start().is_max() || cert.valid_expiry().is_max()) {
+    return CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+  }
+
+  // Scan for the earliest SCT. This is used to determine whether to enforce
+  // log diversity requirements, as well as whether to enforce whether or not
+  // a log was qualified or pending qualification at time of issuance (in the
+  // case of embedded SCTs). It's acceptable to ignore the origin of the SCT,
+  // because SCTs delivered via OCSP/TLS extension will cover the full
+  // certificate, which necessarily will exist only after the precertificate
+  // has been logged and the actual certificate issued.
+  // Note: Here, issuance date is defined as the earliest of all SCTs, rather
+  // than the latest of embedded SCTs, in order to give CAs the benefit of
+  // the doubt in the event a log is revoked in the midst of processing
+  // a precertificate and issuing the certificate.
+  base::Time issuance_date = base::Time::Max();
+  for (const auto& sct : verified_scts) {
+    base::Time unused;
+    if (net::ct::IsLogDisqualified(sct->log_id, &unused))
+      continue;
+    issuance_date = std::min(sct->timestamp, issuance_date);
+  }
+
+  bool has_valid_google_sct = false;
+  bool has_valid_nongoogle_sct = false;
+  bool has_valid_embedded_sct = false;
+  bool has_valid_nonembedded_sct = false;
+  bool has_embedded_google_sct = false;
+  bool has_embedded_nongoogle_sct = false;
+  std::vector<base::StringPiece> embedded_log_ids;
+  for (const auto& sct : verified_scts) {
+    base::Time disqualification_date;
+    bool is_disqualified =
+        net::ct::IsLogDisqualified(sct->log_id, &disqualification_date);
+    if (is_disqualified &&
+        sct->origin != net::ct::SignedCertificateTimestamp::SCT_EMBEDDED) {
+      // For OCSP and TLS delivered SCTs, only SCTs that are valid at the
+      // time of check are accepted.
+      continue;
+    }
+
+    if (net::ct::IsLogOperatedByGoogle(sct->log_id)) {
+      has_valid_google_sct |= !is_disqualified;
+      if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
+        has_embedded_google_sct = true;
+    } else {
+      has_valid_nongoogle_sct |= !is_disqualified;
+      if (sct->origin == net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)
+        has_embedded_nongoogle_sct = true;
+    }
+    if (sct->origin != net::ct::SignedCertificateTimestamp::SCT_EMBEDDED) {
+      has_valid_nonembedded_sct = true;
+    } else {
+      has_valid_embedded_sct |= !is_disqualified;
+      // If the log is disqualified, it only counts towards quorum if
+      // the certificate was issued before the log was disqualified, and the
+      // SCT was obtained before the log was disqualified.
+      if (!is_disqualified || (issuance_date < disqualification_date &&
+                               sct->timestamp < disqualification_date)) {
+        embedded_log_ids.push_back(sct->log_id);
+      }
+    }
+  }
+
+  // Option 1:
+  // An SCT presented via the TLS extension OR embedded within a stapled OCSP
+  //   response is from a log qualified at time of check;
+  // AND there is at least one SCT from a Google Log that is qualified at
+  //   time of check, presented via any method;
+  // AND there is at least one SCT from a non-Google Log that is qualified
+  //   at the time of check, presented via any method.
+  //
+  // Note: Because SCTs embedded via TLS or OCSP can be updated on the fly,
+  // the issuance date is irrelevant, as any policy changes can be
+  // accomodated.
+  if (has_valid_nonembedded_sct && has_valid_google_sct &&
+      has_valid_nongoogle_sct) {
+    return CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+  }
+  // Note: If has_valid_nonembedded_sct was true, but Option 2 isn't met,
+  // then the result will be that there weren't diverse enough SCTs, as that
+  // the only other way for the conditional above to fail). Because Option 1
+  // has the diversity requirement, it's implicitly a minimum number of SCTs
+  // (specifically, 2), but that's not explicitly specified in the policy.
+
+  // Option 2:
+  // There is at least one embedded SCT from a log qualified at the time of
+  //   check ...
+  if (!has_valid_embedded_sct) {
+    // Under Option 2, there weren't enough SCTs, and potentially under
+    // Option 1, there weren't diverse enough SCTs. Try to signal the error
+    // that is most easily fixed.
+    return has_valid_nonembedded_sct
+               ? CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
+               : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+  }
+
+  // ... AND there is at least one embedded SCT from a Google Log once or
+  //   currently qualified;
+  // AND there is at least one embedded SCT from a non-Google Log once or
+  //   currently qualified;
+  // ...
+  //
+  // Note: This policy language is only enforced after the below issuance
+  // date, as that's when the diversity policy first came into effect for
+  // SCTs embedded in certificates.
+  // The date when diverse SCTs requirement is effective from.
+  // 2015-07-01 00:00:00 UTC.
+  const base::Time kDiverseSCTRequirementStartDate =
+      base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1435708800);
+  if (issuance_date >= kDiverseSCTRequirementStartDate &&
+      !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
+    // Note: This also covers the case for non-embedded SCTs, as it's only
+    // possible to reach here if both sets are not diverse enough.
+    return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
+  }
+
+  size_t lifetime_in_months = 0;
+  bool has_partial_month = false;
+  RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(),
+                             &lifetime_in_months, &has_partial_month);
+
+  // ... AND the certificate embeds SCTs from AT LEAST the number of logs
+  //   once or currently qualified shown in Table 1 of the CT Policy.
+  size_t num_required_embedded_scts = 5;
+  if (lifetime_in_months > 39 ||
+      (lifetime_in_months == 39 && has_partial_month)) {
+    num_required_embedded_scts = 5;
+  } else if (lifetime_in_months > 27 ||
+             (lifetime_in_months == 27 && has_partial_month)) {
+    num_required_embedded_scts = 4;
+  } else if (lifetime_in_months >= 15) {
+    num_required_embedded_scts = 3;
+  } else {
+    num_required_embedded_scts = 2;
+  }
+
+  // Sort the embedded log IDs and remove duplicates, so that only a single
+  // SCT from each log is accepted. This is to handle the case where a given
+  // log returns different SCTs for the same precertificate (which is
+  // permitted, but advised against).
+  std::sort(embedded_log_ids.begin(), embedded_log_ids.end());
+  auto sorted_end =
+      std::unique(embedded_log_ids.begin(), embedded_log_ids.end());
+  size_t num_embedded_scts =
+      std::distance(embedded_log_ids.begin(), sorted_end);
+
+  if (num_embedded_scts >= num_required_embedded_scts)
+    return CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
+
+  // Under Option 2, there weren't enough SCTs, and potentially under Option
+  // 1, there weren't diverse enough SCTs. Try to signal the error that is
+  // most easily fixed.
+  return has_valid_nonembedded_sct
+             ? CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
+             : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+}
+
+}  // namespace
+
+CTPolicyCompliance ChromeCTPolicyEnforcer::CheckCompliance(
+    net::X509Certificate* cert,
+    const net::ct::SCTList& verified_scts,
+    const net::NetLogWithSource& net_log) {
+  // If the build is not timely, no certificate is considered compliant
+  // with CT policy. The reasoning is that, for example, a log might
+  // have been pulled and is no longer considered valid; thus, a client
+  // needs up-to-date information about logs to consider certificates to
+  // be compliant with policy.
+  bool build_timely = IsBuildTimely();
+  CTPolicyCompliance compliance;
+  if (!build_timely) {
+    compliance = CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY;
+  } else {
+    compliance = CheckCTPolicyCompliance(*cert, verified_scts);
+  }
+
+  net::NetLogParametersCallback net_log_callback =
+      base::BindRepeating(&NetLogCertComplianceCheckResultCallback,
+                          base::Unretained(cert), build_timely, compliance);
+
+  net_log.AddEvent(net::NetLogEventType::CERT_CT_COMPLIANCE_CHECKED,
+                   net_log_callback);
+
+  return compliance;
+}
+
+}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
new file mode 100644
index 0000000..f61ff0d
--- /dev/null
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -0,0 +1,33 @@
+// 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 COMPONENTS_CERTIFICATE_TRANSPARENCY_CHROME_CT_POLICY_ENFORCER_H_
+#define COMPONENTS_CERTIFICATE_TRANSPARENCY_CHROME_CT_POLICY_ENFORCER_H_
+
+#include "net/cert/ct_policy_enforcer.h"
+
+namespace certificate_transparency {
+
+// A CTPolicyEnforcer that enforces the "Certificate Transparency in Chrome"
+// policies detailed at
+// https://github.com/chromium/ct-policy/blob/master/ct_policy.md
+//
+// This should only be used when there is a reliable, rapid update mechanism
+// for the set of known, qualified logs - either through a reliable binary
+// updating mechanism or through out-of-band delivery. See
+// //net/docs/certificate-transparency.md for more details.
+class ChromeCTPolicyEnforcer : public net::CTPolicyEnforcer {
+ public:
+  ChromeCTPolicyEnforcer() = default;
+  ~ChromeCTPolicyEnforcer() override = default;
+
+  net::ct::CTPolicyCompliance CheckCompliance(
+      net::X509Certificate* cert,
+      const net::ct::SCTList& verified_scts,
+      const net::NetLogWithSource& net_log) override;
+};
+
+}  // namespace certificate_transparency
+
+#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_CHROME_CT_POLICY_ENFORCER_H_
diff --git a/net/cert/ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
similarity index 64%
rename from net/cert/ct_policy_enforcer_unittest.cc
rename to components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index 906cfc9cb..8a5d48ea 100644
--- a/net/cert/ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "net/cert/ct_policy_enforcer.h"
+#include "components/certificate_transparency/chrome_ct_policy_enforcer.h"
 
 #include <memory>
 #include <string>
 
+#include "base/stl_util.h"
 #include "base/time/time.h"
 #include "base/version.h"
 #include "crypto/rsa_private_key.h"
@@ -22,22 +23,28 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace net {
+using net::ct::CTPolicyCompliance;
+using net::ct::SCTList;
+using net::ct::SignedCertificateTimestamp;
+using net::NetLogWithSource;
+using net::X509Certificate;
+
+namespace certificate_transparency {
 
 namespace {
 
 const char kGoogleAviatorLogID[] =
     "\x68\xf6\x98\xf8\x1f\x64\x82\xbe\x3a\x8c\xee\xb9\x28\x1d\x4c\xfc\x71\x51"
     "\x5d\x67\x93\xd4\x44\xd1\x0a\x67\xac\xbb\x4f\x4f\xfb\xc4";
-static_assert(arraysize(kGoogleAviatorLogID) - 1 == crypto::kSHA256Length,
+static_assert(base::size(kGoogleAviatorLogID) - 1 == crypto::kSHA256Length,
               "Incorrect log ID length.");
 
-class CTPolicyEnforcerTest : public ::testing::Test {
+class ChromeCTPolicyEnforcerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    policy_enforcer_.reset(new CTPolicyEnforcer);
+    policy_enforcer_.reset(new ChromeCTPolicyEnforcer);
 
-    std::string der_test_cert(ct::GetDerEncodedX509Cert());
+    std::string der_test_cert(net::ct::GetDerEncodedX509Cert());
     chain_ = X509Certificate::CreateFromBytes(der_test_cert.data(),
                                               der_test_cert.size());
     ASSERT_TRUE(chain_.get());
@@ -46,14 +53,14 @@
   }
 
   void FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::Origin desired_origin,
+      SignedCertificateTimestamp::Origin desired_origin,
       size_t num_scts,
       const std::vector<std::string>& desired_log_keys,
       bool timestamp_past_enforcement_date,
-      ct::SCTList* verified_scts) {
+      SCTList* verified_scts) {
     for (size_t i = 0; i < num_scts; ++i) {
-      scoped_refptr<ct::SignedCertificateTimestamp> sct(
-          new ct::SignedCertificateTimestamp());
+      scoped_refptr<SignedCertificateTimestamp> sct(
+          new SignedCertificateTimestamp());
       sct->origin = desired_origin;
       if (i < desired_log_keys.size())
         sct->log_id = desired_log_keys[i];
@@ -72,18 +79,17 @@
     }
   }
 
-  void AddDisqualifiedLogSCT(
-      ct::SignedCertificateTimestamp::Origin desired_origin,
-      bool timestamp_after_disqualification_date,
-      ct::SCTList* verified_scts) {
+  void AddDisqualifiedLogSCT(SignedCertificateTimestamp::Origin desired_origin,
+                             bool timestamp_after_disqualification_date,
+                             SCTList* verified_scts) {
     static const char kCertlyLogID[] =
         "\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e"
         "\x61\x82\xfa\xf8\x89\x6f\xec\xc8\xb2\xf5\xb5\xab\x60\x49\x00";
-    static_assert(arraysize(kCertlyLogID) - 1 == crypto::kSHA256Length,
+    static_assert(base::size(kCertlyLogID) - 1 == crypto::kSHA256Length,
                   "Incorrect log ID length.");
 
-    scoped_refptr<ct::SignedCertificateTimestamp> sct(
-        new ct::SignedCertificateTimestamp());
+    scoped_refptr<SignedCertificateTimestamp> sct(
+        new SignedCertificateTimestamp());
     sct->origin = desired_origin;
     sct->log_id = std::string(kCertlyLogID, crypto::kSHA256Length);
     if (timestamp_after_disqualification_date) {
@@ -98,9 +104,9 @@
   }
 
   void FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::Origin desired_origin,
+      SignedCertificateTimestamp::Origin desired_origin,
       size_t num_scts,
-      ct::SCTList* verified_scts) {
+      SCTList* verified_scts) {
     std::vector<std::string> desired_log_ids;
     desired_log_ids.push_back(google_log_id_);
     FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids, true,
@@ -116,261 +122,243 @@
   }
 
  protected:
-  std::unique_ptr<CTPolicyEnforcer> policy_enforcer_;
+  std::unique_ptr<net::CTPolicyEnforcer> policy_enforcer_;
   scoped_refptr<X509Certificate> chain_;
   std::string google_log_id_;
   std::string non_google_log_id_;
 };
 
-#if defined(OS_ANDROID)
-#define MAYBE_DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle \
-  DISABLED_DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle
-#else
-#define MAYBE_DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle \
-  DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle
-#endif
-TEST_F(CTPolicyEnforcerTest,
-       MAYBE_DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
-  ct::SCTList scts;
+TEST_F(ChromeCTPolicyEnforcerTest,
+       DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
+  SCTList scts;
   std::vector<std::string> desired_log_ids(2, google_log_id_);
 
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-      desired_log_ids.size(), desired_log_ids, true, &scts);
-
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(CTPolicyEnforcerTest,
-       DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
-  ct::SCTList scts;
-  std::vector<std::string> desired_log_ids(2, non_google_log_id_);
-
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-      desired_log_ids.size(), desired_log_ids, true, &scts);
-
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
-  ct::SCTList scts;
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  // All 5 SCTs will be from non-Google logs.
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
-                           std::vector<std::string>(), false, &scts);
-
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
-  ct::SCTList scts;
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 2, &scts);
-
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  ct::SCTList scts;
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           desired_log_ids.size(), desired_log_ids, true,
                            &scts);
 
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
-  ct::SCTList scts;
+TEST_F(ChromeCTPolicyEnforcerTest,
+       DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
+  SCTList scts;
+  std::vector<std::string> desired_log_ids(2, non_google_log_id_);
+
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           desired_log_ids.size(), desired_log_ids, true,
+                           &scts);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest,
+       ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
+  SCTList scts;
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  // All 5 SCTs will be from non-Google logs.
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+                           std::vector<std::string>(), false, &scts);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           2, &scts);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
+  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+            policy_enforcer_->CheckCompliance(chain_.get(), scts,
+                                              NetLogWithSource()));
+}
+
+TEST_F(ChromeCTPolicyEnforcerTest,
+       ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
+  SCTList scts;
   std::vector<std::string> desired_logs;
 
   // One Google log, delivered via OCSP.
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-      desired_logs.size(), desired_logs, true, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
+                           desired_logs.size(), desired_logs, true, &scts);
 
   // One non-Google log, delivered via TLS.
   desired_logs.clear();
   desired_logs.push_back(non_google_log_id_);
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-      desired_logs.size(), desired_logs, true, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           desired_logs.size(), desired_logs, true, &scts);
 
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithPooledEmbeddedSCTs) {
-  ct::SCTList scts;
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyWithPooledEmbeddedSCTs) {
+  SCTList scts;
   std::vector<std::string> desired_logs;
 
   // One Google log, delivered embedded.
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
                            desired_logs.size(), desired_logs, true, &scts);
 
   // One non-Google log, delivered via OCSP.
   desired_logs.clear();
   desired_logs.push_back(non_google_log_id_);
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-      desired_logs.size(), desired_logs, true, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
+                           desired_logs.size(), desired_logs, true, &scts);
 
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  ct::SCTList scts;
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 2,
-                           &scts);
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
 
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
-  ct::SCTList scts;
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
+  SCTList scts;
 
   // The results should be the same before and after disqualification,
   // regardless of the delivery method.
 
   // SCT from before disqualification.
   scts.clear();
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 1, &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                         false, &scts);
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 
   // SCT from after disqualification.
   scts.clear();
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 1, &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                         true, &scts);
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 
   // Embedded SCT from before disqualification.
   scts.clear();
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 1, &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_EMBEDDED, false,
-                        &scts);
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 
   // Embedded SCT from after disqualification.
   scts.clear();
-  FillListWithSCTsOfOrigin(
-      ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 1, &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_EMBEDDED, true,
-                        &scts);
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
+                           1, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTest,
        ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
-  ct::SCTList scts;
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 4,
-                           &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_EMBEDDED, false,
-                        &scts);
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
 
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformWithDisqualifiedLogAfterDisqualificationDate) {
-  ct::SCTList scts;
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 4,
-                           &scts);
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_EMBEDDED, true,
-                        &scts);
+  SCTList scts;
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
 
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
-  ct::SCTList scts;
-  AddDisqualifiedLogSCT(ct::SignedCertificateTimestamp::SCT_EMBEDDED, true,
-                        &scts);
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 4,
-                           &scts);
+  SCTList scts;
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
   // Make sure all SCTs are after the disqualification date.
   for (size_t i = 1; i < scts.size(); ++i)
     scts[i]->timestamp = scts[0]->timestamp;
 
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
-  ct::SCTList scts;
+  SCTList scts;
   std::vector<std::string> desired_logs;
 
   // One Google Log.
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
                            desired_logs.size(), desired_logs, true, &scts);
 
   // Two distinct non-Google logs.
   desired_logs.clear();
   desired_logs.push_back(std::string(crypto::kSHA256Length, 'A'));
   desired_logs.push_back(std::string(crypto::kSHA256Length, 'B'));
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
                            desired_logs.size(), desired_logs, true, &scts);
 
   // Two unique SCTs from the same non-Google log.
   desired_logs.clear();
   desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
   desired_logs.push_back(std::string(crypto::kSHA256Length, 'C'));
-  FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
                            desired_logs.size(), desired_logs, true, &scts);
 
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
   // However, there are only 4 SCTs are from distinct logs.
-  EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
 }
 
-TEST_F(CTPolicyEnforcerTest,
+TEST_F(ChromeCTPolicyEnforcerTest,
        ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
   std::unique_ptr<crypto::RSAPrivateKey> private_key(
       crypto::RSAPrivateKey::Create(1024));
@@ -422,7 +410,7 @@
                    {// Cert valid for over 39 months, needs 5 SCTs.
                     time_2015_3_0_25_11_25_0_0, time_2018_6_0_27_11_25_0_0, 5}};
 
-  for (size_t i = 0; i < arraysize(kTestData); ++i) {
+  for (size_t i = 0; i < base::size(kTestData); ++i) {
     SCOPED_TRACE(i);
     const base::Time& start = kTestData[i].validity_start;
     const base::Time& end = kTestData[i].validity_end;
@@ -430,29 +418,29 @@
 
     // Create a self-signed certificate with exactly the validity period.
     std::string cert_data;
-    ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-        private_key->key(), x509_util::DIGEST_SHA256, "CN=test",
+    ASSERT_TRUE(net::x509_util::CreateSelfSignedCert(
+        private_key->key(), net::x509_util::DIGEST_SHA256, "CN=test",
         i * 10 + required_scts, start, end, &cert_data));
     scoped_refptr<X509Certificate> cert(
         X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size()));
     ASSERT_TRUE(cert);
 
     for (size_t i = 0; i < required_scts - 1; ++i) {
-      ct::SCTList scts;
-      FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, i,
+      SCTList scts;
+      FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, i,
                                std::vector<std::string>(), false, &scts);
-      EXPECT_EQ(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
+      EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
                 policy_enforcer_->CheckCompliance(cert.get(), scts,
                                                   NetLogWithSource()))
           << " for: " << (end - start).InDays() << " and " << required_scts
           << " scts=" << scts.size() << " i=" << i;
     }
-    ct::SCTList scts;
-    FillListWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
+    SCTList scts;
+    FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
                              required_scts, std::vector<std::string>(), false,
                              &scts);
     EXPECT_EQ(
-        ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
+        CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
         policy_enforcer_->CheckCompliance(cert.get(), scts, NetLogWithSource()))
         << " for: " << (end - start).InDays() << " and " << required_scts
         << " scts=" << scts.size();
@@ -461,4 +449,4 @@
 
 }  // namespace
 
-}  // namespace net
+}  // namespace certificate_transparency
diff --git a/components/client_update_protocol/ecdsa_unittest.cc b/components/client_update_protocol/ecdsa_unittest.cc
index 8c1e933..71c7441 100644
--- a/components/client_update_protocol/ecdsa_unittest.cc
+++ b/components/client_update_protocol/ecdsa_unittest.cc
@@ -46,7 +46,7 @@
     ASSERT_TRUE(cup_.get());
   }
 
-  Ecdsa& CUP() { return *cup_.get(); }
+  Ecdsa& CUP() { return *cup_; }
 
  private:
   std::unique_ptr<Ecdsa> cup_;
diff --git a/components/component_updater/component_installer.cc b/components/component_updater/component_installer.cc
index d428b217..285f2739 100644
--- a/components/component_updater/component_installer.cc
+++ b/components/component_updater/component_installer.cc
@@ -250,7 +250,7 @@
 void ComponentInstaller::StartRegistration(
     scoped_refptr<RegistrationInfo> registration_info) {
   VLOG(1) << __func__ << " for " << installer_policy_->GetName();
-  DCHECK(task_runner_.get());
+  DCHECK(task_runner_);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   base::Version latest_version(kNullVersion);
@@ -353,7 +353,7 @@
 }
 
 void ComponentInstaller::UninstallOnTaskRunner() {
-  DCHECK(task_runner_.get());
+  DCHECK(task_runner_);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   // Only try to delete any files that are in our user-level install path.
diff --git a/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc b/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
index 8bb2f986..a8758321 100644
--- a/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
+++ b/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
@@ -41,7 +41,7 @@
     DCHECK(current_rule_->second.value.get());
     Rule to_return(current_rule_->first.primary_pattern,
                    current_rule_->first.secondary_pattern,
-                   current_rule_->second.value.get()->DeepCopy());
+                   current_rule_->second.value->DeepCopy());
     ++current_rule_;
     return to_return;
   }
@@ -83,7 +83,7 @@
          std::tie(other.primary_pattern, other.secondary_pattern);
 }
 
-OriginIdentifierValueMap::ValueEntry::ValueEntry() : last_modified(), value(){};
+OriginIdentifierValueMap::ValueEntry::ValueEntry(){};
 
 OriginIdentifierValueMap::ValueEntry::~ValueEntry(){};
 
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index 7f91a3c..4565d0f 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -130,7 +130,7 @@
 
   {
     base::AutoLock auto_lock(lock_);
-    if (value.get()) {
+    if (value) {
       map_to_modify->SetValue(primary_pattern, secondary_pattern, content_type_,
                               resource_identifier, modified_time,
                               value->DeepCopy());
diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc
index 1c23860..a1252ad4 100644
--- a/components/content_settings/core/browser/cookie_settings.cc
+++ b/components/content_settings/core/browser/cookie_settings.cc
@@ -189,7 +189,7 @@
       net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
 
   // We should always have a value, at least from the default provider.
-  DCHECK(value.get());
+  DCHECK(value);
   ContentSetting setting = ValueToContentSetting(value.get());
   bool block =
       block_third && policy.CanAccessCookies(url, first_party_url) != net::OK;
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index c14f0919..d45b95a 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -850,7 +850,7 @@
           *primary_pattern = rule.primary_pattern;
         if (secondary_pattern)
           *secondary_pattern = rule.secondary_pattern;
-        return base::WrapUnique(rule.value.get()->DeepCopy());
+        return base::WrapUnique(rule.value->DeepCopy());
       }
     }
   }
diff --git a/components/contextual_search/browser/ctr_aggregator_unittest.cc b/components/contextual_search/browser/ctr_aggregator_unittest.cc
index 9e3d791..1fc9e49e 100644
--- a/components/contextual_search/browser/ctr_aggregator_unittest.cc
+++ b/components/contextual_search/browser/ctr_aggregator_unittest.cc
@@ -48,7 +48,7 @@
 
   void SetUp() override {
     storage_.reset(new WeeklyActivityStorageStub());
-    aggregator_.reset(new CtrAggregator(*storage_.get(), kTestWeek));
+    aggregator_.reset(new CtrAggregator(*storage_, kTestWeek));
   }
 
   void TearDown() override {}
diff --git a/components/crash/content/app/breakpad_win.cc b/components/crash/content/app/breakpad_win.cc
index 06e38f9..382b79e 100644
--- a/components/crash/content/app/breakpad_win.cc
+++ b/components/crash/content/app/breakpad_win.cc
@@ -91,8 +91,8 @@
 // This is the well known SID for the system principal.
 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
 
-google_breakpad::ExceptionHandler* g_breakpad = NULL;
-google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL;
+google_breakpad::ExceptionHandler* g_breakpad = nullptr;
+google_breakpad::ExceptionHandler* g_dumphandler_no_crash = nullptr;
 
 #if !defined(_WIN64)
 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0};
@@ -102,7 +102,7 @@
 
 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
                                                  NTSTATUS ExitStatus);
-char* g_real_terminate_process_stub = NULL;
+char* g_real_terminate_process_stub = nullptr;
 
 }  // namespace
 
@@ -133,8 +133,8 @@
     HANDLE process) {
   // |serialized_crash_keys| is not propagated in breakpad but is in crashpad
   // since breakpad is deprecated.
-  return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread,
-                            0, 0, NULL);
+  return CreateRemoteThread(process, nullptr, 0, DumpProcessWithoutCrashThread,
+                            nullptr, 0, nullptr);
 }
 
 // Returns a string containing a list of all modifiers for the loaded profile.
@@ -198,8 +198,9 @@
   // Now we just start chrome browser with the same command line.
   STARTUPINFOW si = {sizeof(si)};
   PROCESS_INFORMATION pi;
-  if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE,
-                       CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) {
+  if (::CreateProcessW(nullptr, ::GetCommandLineW(), nullptr, nullptr, FALSE,
+                       CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &si,
+                       &pi)) {
     ::CloseHandle(pi.hProcess);
     ::CloseHandle(pi.hThread);
   }
@@ -234,12 +235,12 @@
 
 // Previous unhandled filter. Will be called if not null when we
 // intercept a crash.
-LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL;
+LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = nullptr;
 
 // Exception filter used when breakpad is not enabled. We just display
 // the "Do you want to restart" message and then we call the previous filter.
 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
-  DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
+  DumpDoneCallback(nullptr, nullptr, nullptr, info, nullptr, false);
 
   if (previous_filter)
     return previous_filter(info);
@@ -251,7 +252,7 @@
 // not enabled. We just display the "Do you want to restart" message and then
 // die (without calling the previous filter).
 long WINAPI CloudPrintServiceExceptionFilter(EXCEPTION_POINTERS* info) {
-  DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
+  DumpDoneCallback(nullptr, nullptr, nullptr, info, nullptr, false);
   return EXCEPTION_EXECUTE_HANDLER;
 }
 
@@ -289,7 +290,7 @@
   // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
   // uncontrollably here. Being this a best effort deal we better go away.
   __try {
-    *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
+    *exit_now = (IDOK != ::MessageBoxW(nullptr, text, caption, flags));
   } __except(EXCEPTION_EXECUTE_HANDLER) {
     // Its not safe to continue executing, exit silently here.
     ::TerminateProcess(::GetCurrentProcess(),
@@ -324,7 +325,7 @@
   // Patched stub exists based on conditions (See InitCrashReporter).
   // As a side note this function also gets called from
   // WindowProcExceptionFilter.
-  if (g_real_terminate_process_stub == NULL) {
+  if (g_real_terminate_process_stub == nullptr) {
     ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
   } else {
     NtTerminateProcessPtr real_terminate_proc =
@@ -488,7 +489,7 @@
 
   wchar_t exe_path[MAX_PATH];
   exe_path[0] = 0;
-  GetModuleFileNameW(NULL, exe_path, MAX_PATH);
+  GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
 
   // This is intentionally leaked.
   CrashKeysWin* keeper = new CrashKeysWin();
@@ -498,8 +499,8 @@
                             base::CommandLine::ForCurrentProcess(),
                             GetCrashReporterClient());
 
-  google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
-  LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
+  google_breakpad::ExceptionHandler::MinidumpCallback callback = nullptr;
+  LPTOP_LEVEL_EXCEPTION_FILTER default_filter = nullptr;
   // We install the post-dump callback only for the browser and service
   // processes. It spawns a new browser/service process.
   if (process_type == L"browser") {
@@ -546,21 +547,20 @@
   else if (GetCrashReporterClient()->GetShouldDumpLargerDumps())
     dump_type = kLargerDumpType;
 
-  g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback,
-                   callback, NULL,
-                   google_breakpad::ExceptionHandler::HANDLER_ALL,
-                   dump_type, pipe_name.c_str(), custom_info);
+  g_breakpad = new google_breakpad::ExceptionHandler(
+      temp_dir, &FilterCallback, callback, nullptr,
+      google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
+      pipe_name.c_str(), custom_info);
 
   // Now initialize the non crash dump handler.
-  g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir,
-      &FilterCallbackWhenNoCrash,
-      &DumpDoneCallbackWhenNoCrash,
-      NULL,
+  g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(
+      temp_dir, &FilterCallbackWhenNoCrash, &DumpDoneCallbackWhenNoCrash,
+      nullptr,
       // Set the handler to none so this handler would not be added to
       // |handler_stack_| in |ExceptionHandler| which is a list of exception
       // handlers.
-      google_breakpad::ExceptionHandler::HANDLER_NONE,
-      dump_type, pipe_name.c_str(), custom_info);
+      google_breakpad::ExceptionHandler::HANDLER_NONE, dump_type,
+      pipe_name.c_str(), custom_info);
 
   // Set the DumpWithoutCrashingFunction for this instance of base.lib.  Other
   // executable images linked with base should set this again for
diff --git a/components/crash/content/app/crash_keys_win.cc b/components/crash/content/app/crash_keys_win.cc
index 7df7135..bcf8f10 100644
--- a/components/crash/content/app/crash_keys_win.cc
+++ b/components/crash/content/app/crash_keys_win.cc
@@ -36,7 +36,7 @@
 
 CrashKeysWin::~CrashKeysWin() {
   DCHECK_EQ(this, keeper_);
-  keeper_ = NULL;
+  keeper_ = nullptr;
 }
 
 // Appends the plugin path to |g_custom_entries|.
@@ -167,7 +167,7 @@
   base::AutoLock lock(lock_);
 
   DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key);
-  google_breakpad::CustomInfoEntry* entry = NULL;
+  google_breakpad::CustomInfoEntry* entry = nullptr;
   if (it == dynamic_entries_.end()) {
     if (dynamic_entries_.size() >= kMaxDynamicEntries)
       return;
@@ -188,7 +188,7 @@
   if (it == dynamic_entries_.end())
     return;
 
-  it->second->set_value(NULL);
+  it->second->set_value(nullptr);
 }
 
 }  // namespace breakpad
diff --git a/components/crash/content/app/crash_keys_win_unittest.cc b/components/crash/content/app/crash_keys_win_unittest.cc
index f7c387f..c9ad5c9 100644
--- a/components/crash/content/app/crash_keys_win_unittest.cc
+++ b/components/crash/content/app/crash_keys_win_unittest.cc
@@ -123,8 +123,8 @@
                                &cmd_line,
                                &crash_client_);
 
-  ASSERT_TRUE(info != NULL);
-  ASSERT_TRUE(info->entries != NULL);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_TRUE(info->entries != nullptr);
 
   // We expect 7 fixed keys and a "freeboard" of 256 keys for dynamic entries.
   EXPECT_EQ(256U + 7U, info->count);
diff --git a/components/crash/content/app/hard_error_handler_win.cc b/components/crash/content/app/hard_error_handler_win.cc
index 11eca1c..dec998f 100644
--- a/components/crash/content/app/hard_error_handler_win.cc
+++ b/components/crash/content/app/hard_error_handler_win.cc
@@ -41,16 +41,12 @@
     return;
 
   HMODULE ntdll = ::GetModuleHandleA("NTDLL.DLL");
-  wchar_t* msg_template = NULL;
+  wchar_t* msg_template = nullptr;
   size_t count = ::FormatMessage(
       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS |
-      FORMAT_MESSAGE_FROM_HMODULE,
-      ntdll,
-      nt_status,
-      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-      reinterpret_cast<wchar_t*>(&msg_template),
-      0,
-      NULL);
+          FORMAT_MESSAGE_FROM_HMODULE,
+      ntdll, nt_status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+      reinterpret_cast<wchar_t*>(&msg_template), 0, nullptr);
 
   if (!count)
     return;
@@ -61,8 +57,7 @@
   // The MB_SERVICE_NOTIFICATION causes this message to be displayed by
   // csrss. This means that we are not creating windows or pumping WM messages
   // in this process.
-  ::MessageBox(NULL, message.c_str(),
-               L"chrome.exe",
+  ::MessageBox(nullptr, message.c_str(), L"chrome.exe",
                MB_OK | MB_SERVICE_NOTIFICATION);
   ::LocalFree(msg_template);
 }
diff --git a/components/crash_strings_grdp/OWNERS b/components/crash_strings_grdp/OWNERS
new file mode 100644
index 0000000..21fa106
--- /dev/null
+++ b/components/crash_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/crash/OWNERS
diff --git a/components/crash_strings_grdp/README.md b/components/crash_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/crash_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/cronet/ios/cronet_metrics.h b/components/cronet/ios/cronet_metrics.h
index f21b5bf..f8a25dd 100644
--- a/components/cronet/ios/cronet_metrics.h
+++ b/components/cronet/ios/cronet_metrics.h
@@ -97,13 +97,8 @@
   // nullptr.
   static std::unique_ptr<Metrics> MetricsForTask(NSURLSessionTask* task);
 
-  // Used by tests to query the size of the |task_metrics_map_| map.
+  // Used by tests to query the size of the |gTaskMetricsMap| map.
   static size_t GetMetricsMapSize();
-
- private:
-  static NSObject* task_metrics_map_lock_;
-  static std::map<NSURLSessionTask*, std::unique_ptr<Metrics>>
-      task_metrics_map_;
 };
 
 // This is the swizzling function that Cronet (in its startInternal
diff --git a/components/cronet/ios/cronet_metrics.mm b/components/cronet/ios/cronet_metrics.mm
index e121237..cce453ad 100644
--- a/components/cronet/ios/cronet_metrics.mm
+++ b/components/cronet/ios/cronet_metrics.mm
@@ -6,6 +6,7 @@
 
 #include <objc/runtime.h>
 
+#include "base/lazy_instance.h"
 #include "base/strings/sys_string_conversions.h"
 
 @implementation CronetTransactionMetrics
@@ -92,6 +93,17 @@
 
 using Metrics = net::MetricsDelegate::Metrics;
 
+// Synchronizes access to |gTaskMetricsMap|.
+base::LazyInstance<base::Lock>::Leaky gTaskMetricsMapLock =
+    LAZY_INSTANCE_INITIALIZER;
+
+// A global map that contains metrics information for pending URLSessionTasks.
+// The map has to be "leaky"; otherwise, it will be destroyed on the main thread
+// when the client app terminates. When the client app terminates, the network
+// thread may still be finishing some work that requires access to the map.
+base::LazyInstance<std::map<NSURLSessionTask*, std::unique_ptr<Metrics>>>::Leaky
+    gTaskMetricsMap = LAZY_INSTANCE_INITIALIZER;
+
 // Helper method that converts the ticks data found in LoadTimingInfo to an
 // NSDate value to be used in client-side data.
 NSDate* TicksToDate(const net::LoadTimingInfo& reference,
@@ -272,7 +284,7 @@
   // Regardless whether the underlying session delegate handles
   // URLSession:task:didFinishCollectingMetrics: or not, always
   // return 'YES' for that selector. Otherwise, the method may
-  // not be called, causing unbounded growth of |task_metrics_map_|.
+  // not be called, causing unbounded growth of |gTaskMetricsMap|.
   if (aSelector == @selector(URLSession:task:didFinishCollectingMetrics:)) {
     return YES;
   }
@@ -301,52 +313,42 @@
 
 namespace cronet {
 
-// static
-NSObject* CronetMetricsDelegate::task_metrics_map_lock_ =
-    [[NSObject alloc] init];
-std::map<NSURLSessionTask*, std::unique_ptr<Metrics>>
-    CronetMetricsDelegate::task_metrics_map_;
-
 std::unique_ptr<Metrics> CronetMetricsDelegate::MetricsForTask(
     NSURLSessionTask* task) {
-  @synchronized(task_metrics_map_lock_) {
-    auto metrics_search = task_metrics_map_.find(task);
-    if (metrics_search == task_metrics_map_.end()) {
-      return nullptr;
-    }
-
-    std::unique_ptr<Metrics> metrics = std::move(metrics_search->second);
-    // Remove the entry to free memory.
-    task_metrics_map_.erase(metrics_search);
-
-    return metrics;
+  base::AutoLock auto_lock(gTaskMetricsMapLock.Get());
+  auto metrics_search = gTaskMetricsMap.Get().find(task);
+  if (metrics_search == gTaskMetricsMap.Get().end()) {
+    return nullptr;
   }
+
+  std::unique_ptr<Metrics> metrics = std::move(metrics_search->second);
+  // Remove the entry to free memory.
+  gTaskMetricsMap.Get().erase(metrics_search);
+
+  return metrics;
 }
 
 void CronetMetricsDelegate::OnStartNetRequest(NSURLSessionTask* task) {
   if (@available(iOS 10, *)) {
-    @synchronized(task_metrics_map_lock_) {
-      if ([task state] == NSURLSessionTaskStateRunning) {
-        task_metrics_map_[task] = nullptr;
-      }
+    base::AutoLock auto_lock(gTaskMetricsMapLock.Get());
+    if ([task state] == NSURLSessionTaskStateRunning) {
+      gTaskMetricsMap.Get()[task] = nullptr;
     }
   }
 }
 
 void CronetMetricsDelegate::OnStopNetRequest(std::unique_ptr<Metrics> metrics) {
   if (@available(iOS 10, *)) {
-    @synchronized(task_metrics_map_lock_) {
-      auto metrics_search = task_metrics_map_.find(metrics->task);
-      if (metrics_search != task_metrics_map_.end())
-        metrics_search->second = std::move(metrics);
-    }
+    base::AutoLock auto_lock(gTaskMetricsMapLock.Get());
+    auto metrics_search = gTaskMetricsMap.Get().find(metrics->task);
+    if (metrics_search != gTaskMetricsMap.Get().end())
+      metrics_search->second = std::move(metrics);
   }
 }
 
 size_t CronetMetricsDelegate::GetMetricsMapSize() {
-  @synchronized(task_metrics_map_lock_) {
-    return task_metrics_map_.size();
-  }
+  base::AutoLock auto_lock(gTaskMetricsMapLock.Get());
+  return gTaskMetricsMap.Get().size();
 }
 
 #pragma mark - Swizzle
diff --git a/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc b/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
index 08bf09e..45b7f9a0 100644
--- a/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
@@ -66,7 +66,7 @@
   CHECK(test);
   test->GetData_called_ = true;
 
-  return static_cast<Cronet_RawDataPtr>(0);
+  return static_cast<Cronet_RawDataPtr>(nullptr);
 }
 }  // namespace
 
@@ -276,7 +276,7 @@
   CHECK(test);
   test->GetVersionString_called_ = true;
 
-  return static_cast<Cronet_String>(0);
+  return static_cast<Cronet_String>(nullptr);
 }
 Cronet_String TestCronet_Engine_GetDefaultUserAgent(Cronet_EnginePtr self) {
   CHECK(self);
@@ -285,7 +285,7 @@
   CHECK(test);
   test->GetDefaultUserAgent_called_ = true;
 
-  return static_cast<Cronet_String>(0);
+  return static_cast<Cronet_String>(nullptr);
 }
 }  // namespace
 
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index 3f014e30..3ce28df 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -361,10 +361,7 @@
 }
 
 StaleHostResolver::StaleOptions::StaleOptions()
-    : delay(),
-      max_expired_time(),
-      allow_other_network(false),
-      max_stale_uses(0) {}
+    : allow_other_network(false), max_stale_uses(0) {}
 
 StaleHostResolver::StaleHostResolver(
     std::unique_ptr<net::HostResolverImpl> inner_resolver,
diff --git a/components/cronet/test/test_server.cc b/components/cronet/test/test_server.cc
index 6b6b164..bba65bfa 100644
--- a/components/cronet/test/test_server.cc
+++ b/components/cronet/test/test_server.cc
@@ -199,13 +199,13 @@
 
 /* static */
 int TestServer::GetPort() {
-  DCHECK(g_test_server.get());
+  DCHECK(g_test_server);
   return g_test_server->port();
 }
 
 /* static */
 std::string TestServer::GetHostPort() {
-  DCHECK(g_test_server.get());
+  DCHECK(g_test_server);
   return net::HostPortPair::FromURL(g_test_server->base_url()).ToString();
 }
 
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index 72130a5..3c0083c3 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -126,20 +126,6 @@
 
 const char kSSLKeyLogFile[] = "ssl_key_log_file";
 
-// A CTPolicyEnforcer that accepts all certificates.
-class DoNothingCTPolicyEnforcer : public net::CTPolicyEnforcer {
- public:
-  DoNothingCTPolicyEnforcer() = default;
-  ~DoNothingCTPolicyEnforcer() override = default;
-
-  net::ct::CTPolicyCompliance CheckCompliance(
-      net::X509Certificate* cert,
-      const net::SCTList& verified_scts,
-      const net::NetLogWithSource& net_log) override {
-    return net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-};
-
 }  // namespace
 
 URLRequestContextConfig::QuicHint::QuicHint(const std::string& host,
@@ -231,8 +217,7 @@
   StaleHostResolver::StaleOptions stale_dns_options;
   std::string host_resolver_rules_string;
 
-  for (base::DictionaryValue::Iterator it(*dict.get()); !it.IsAtEnd();
-       it.Advance()) {
+  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
     if (it.key() == kQuicFieldTrialName) {
       const base::DictionaryValue* quic_args = nullptr;
       if (!it.value().GetAsDictionary(&quic_args)) {
@@ -591,7 +576,7 @@
   context_builder->set_ct_verifier(
       std::make_unique<net::DoNothingCTVerifier>());
   context_builder->set_ct_policy_enforcer(
-      std::make_unique<DoNothingCTPolicyEnforcer>());
+      std::make_unique<net::DefaultCTPolicyEnforcer>());
   // TODO(mef): Use |config| to set cookies.
 }
 
diff --git a/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc b/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
index ed6203c..8c11b05 100644
--- a/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
+++ b/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc
@@ -230,15 +230,15 @@
 
   std::unique_ptr<net::URLRequest> request =
       CreateRequest(true /* is main */, content::PREVIEWS_OFF);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequest(true /* is main */, content::PREVIEWS_NO_TRANSFORM);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequest(true /* is main */, content::PREVIEWS_UNSPECIFIED);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 }
 
@@ -255,12 +255,12 @@
   std::unique_ptr<net::URLRequest> request =
       CreateRequestByType(content::RESOURCE_TYPE_MAIN_FRAME, true /* https */,
                           both_previews_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequestByType(content::RESOURCE_TYPE_IMAGE, true /* https */,
                                 both_previews_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 }
 
@@ -275,11 +275,11 @@
   // Verify accepting lite-page per resource type.
   std::unique_ptr<net::URLRequest> request =
       CreateRequest(true /* is main */, lite_page_enabled);
-  VerifyAcceptTransformHeader(*request.get(), true /* lite-page */,
+  VerifyAcceptTransformHeader(*request, true /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequest(false /* is main */, lite_page_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 }
 
@@ -294,27 +294,27 @@
   // Verify accepting empty-image per resource type.
   std::unique_ptr<net::URLRequest> request = CreateRequestByType(
       content::RESOURCE_TYPE_MAIN_FRAME, false /* https */, lofi_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequestByType(content::RESOURCE_TYPE_IMAGE, false /* https */,
                                 lofi_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               true /* empty-image */);
 
   request = CreateRequestByType(content::RESOURCE_TYPE_FAVICON,
                                 false /* https */, lofi_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               true /* empty-image */);
 
   request = CreateRequestByType(content::RESOURCE_TYPE_SCRIPT,
                                 false /* https */, lofi_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 
   request = CreateRequestByType(content::RESOURCE_TYPE_STYLESHEET,
                                 false /* https */, lofi_enabled);
-  VerifyAcceptTransformHeader(*request.get(), false /* lite-page */,
+  VerifyAcceptTransformHeader(*request, false /* lite-page */,
                               false /* empty-image */);
 }
 
@@ -466,13 +466,13 @@
       new data_reduction_proxy::ContentLoFiDecider());
   std::unique_ptr<net::URLRequest> request1 = CreateRequestByType(
       content::RESOURCE_TYPE_IMAGE, false, content::SERVER_LOFI_ON);
-  EXPECT_TRUE(lofi_decider->ShouldRecordLoFiUMA(*request1.get()));
+  EXPECT_TRUE(lofi_decider->ShouldRecordLoFiUMA(*request1));
   std::unique_ptr<net::URLRequest> request2 = CreateRequestByType(
       content::RESOURCE_TYPE_MAIN_FRAME, false, content::PREVIEWS_OFF);
-  EXPECT_FALSE(lofi_decider->ShouldRecordLoFiUMA(*request2.get()));
+  EXPECT_FALSE(lofi_decider->ShouldRecordLoFiUMA(*request2));
   std::unique_ptr<net::URLRequest> request3 = CreateRequestByType(
       content::RESOURCE_TYPE_MAIN_FRAME, false, content::SERVER_LITE_PAGE_ON);
-  EXPECT_TRUE(lofi_decider->ShouldRecordLoFiUMA(*request3.get()));
+  EXPECT_TRUE(lofi_decider->ShouldRecordLoFiUMA(*request3));
 }
 
 TEST_F(ContentLoFiDeciderTest, NoTransformDoesNotAddHeader) {
diff --git a/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc b/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
index f6fc2463..025379c9 100644
--- a/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
+++ b/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
@@ -337,7 +337,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   PageloadMetrics* pageload_metrics = metrics_request_.add_pageloads();
   AddDataToPageloadMetrics(request_data, timing, crash_type, pageload_metrics);
-  if (current_fetcher_.get())
+  if (current_fetcher_)
     return;
   DCHECK_EQ(1, metrics_request_.pageloads_size());
   CreateFetcherForDataAndStart();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
index bc20df0d..460aceb4 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -15,6 +15,8 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "net/base/load_flags.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_response_headers.h"
@@ -33,38 +35,38 @@
 
 namespace {
 
-// Adds non-empty entries in |data_reduction_proxies| to the retry map
-// maintained by the proxy service of the request. Adds
-// |data_reduction_proxies.second| to the retry list only if |bypass_all| is
-// true.
-void MarkProxiesAsBadUntil(
-    net::URLRequest* request,
-    const base::TimeDelta& bypass_duration,
-    bool bypass_all,
-    const std::vector<net::ProxyServer>& data_reduction_proxies) {
-  DCHECK(!data_reduction_proxies.empty());
+// Adds the Data Reduction Proxy servers in |proxy_type_info| that should be
+// marked bad according to |data_reduction_proxy_info| to the retry map
+// maintained by the proxy resolution service of the |request|.
+void MarkProxiesAsBad(const net::URLRequest& request,
+                      const DataReductionProxyInfo& data_reduction_proxy_info,
+                      const DataReductionProxyTypeInfo& proxy_type_info) {
+  DCHECK_GT(proxy_type_info.proxy_servers.size(), proxy_type_info.proxy_index);
+
   // Synthesize a suitable |ProxyInfo| to add the proxies to the
   // |ProxyRetryInfoMap| of the proxy service.
   net::ProxyList proxy_list;
-  std::vector<net::ProxyServer> additional_bad_proxies;
-  for (const net::ProxyServer& proxy_server : data_reduction_proxies) {
-    DCHECK(proxy_server.is_valid());
-    proxy_list.AddProxyServer(proxy_server);
-    if (!bypass_all)
-      break;
-    additional_bad_proxies.push_back(proxy_server);
+
+  const size_t bad_proxy_end_index = data_reduction_proxy_info.bypass_all
+                                         ? proxy_type_info.proxy_servers.size()
+                                         : proxy_type_info.proxy_index + 1U;
+
+  for (size_t i = proxy_type_info.proxy_index; i < bad_proxy_end_index; ++i) {
+    const net::ProxyServer& bad_proxy =
+        proxy_type_info.proxy_servers[i].proxy_server();
+    DCHECK(bad_proxy.is_valid());
+    DCHECK(!bad_proxy.is_direct());
+    proxy_list.AddProxyServer(bad_proxy);
   }
+  std::vector<net::ProxyServer> bad_proxies = proxy_list.GetAll();
   proxy_list.AddProxyServer(net::ProxyServer::Direct());
 
   net::ProxyInfo proxy_info;
   proxy_info.UseProxyList(proxy_list);
-  DCHECK(request->context());
-  net::ProxyResolutionService* proxy_resolution_service =
-      request->context()->proxy_resolution_service();
-  DCHECK(proxy_resolution_service);
 
-  proxy_resolution_service->MarkProxiesAsBadUntil(
-      proxy_info, bypass_duration, additional_bad_proxies, request->net_log());
+  request.context()->proxy_resolution_service()->MarkProxiesAsBadUntil(
+      proxy_info, data_reduction_proxy_info.bypass_duration, bad_proxies,
+      request.net_log());
 }
 
 }  // namespace
@@ -85,7 +87,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(request);
 
-  DataReductionProxyTypeInfo data_reduction_proxy_type_info;
   DataReductionProxyBypassType bypass_type;
 
   const net::HttpResponseHeaders* response_headers =
@@ -93,21 +94,19 @@
   bool retry;
   if (!response_headers) {
     retry = HandleInvalidResponseHeadersCase(
-        *request, data_reduction_proxy_info, &data_reduction_proxy_type_info,
-        &bypass_type);
-
+        *request, data_reduction_proxy_info, &bypass_type);
   } else {
     retry = HandleValidResponseHeadersCase(
-        *request, proxy_bypass_type, data_reduction_proxy_info,
-        &data_reduction_proxy_type_info, &bypass_type);
+        *request, proxy_bypass_type, data_reduction_proxy_info, &bypass_type);
   }
   if (!retry)
     return false;
 
   if (data_reduction_proxy_info->mark_proxies_as_bad) {
-    MarkProxiesAsBadUntil(request, data_reduction_proxy_info->bypass_duration,
-                          data_reduction_proxy_info->bypass_all,
-                          data_reduction_proxy_type_info.proxy_servers);
+    base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+        config_->FindConfiguredDataReductionProxy(request->proxy_server());
+    DCHECK(proxy_type_info);
+    MarkProxiesAsBad(*request, *data_reduction_proxy_info, *proxy_type_info);
   } else {
     request->SetLoadFlags(request->load_flags() | net::LOAD_BYPASS_CACHE |
                           net::LOAD_BYPASS_PROXY);
@@ -120,7 +119,6 @@
 bool DataReductionProxyBypassProtocol::HandleInvalidResponseHeadersCase(
     const net::URLRequest& request,
     DataReductionProxyInfo* data_reduction_proxy_info,
-    DataReductionProxyTypeInfo* data_reduction_proxy_type_info,
     DataReductionProxyBypassType* bypass_type) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_EQ(nullptr, request.response_info().headers.get());
@@ -132,10 +130,8 @@
     return false;
   }
 
-  if (!config_->WasDataReductionProxyUsed(&request,
-                                          data_reduction_proxy_type_info)) {
+  if (!config_->FindConfiguredDataReductionProxy(request.proxy_server()))
     return false;
-  }
 
   DCHECK(request.url().SchemeIs(url::kHttpScheme));
 
@@ -179,7 +175,6 @@
     const net::URLRequest& request,
     DataReductionProxyBypassType* proxy_bypass_type,
     DataReductionProxyInfo* data_reduction_proxy_info,
-    DataReductionProxyTypeInfo* data_reduction_proxy_type_info,
     DataReductionProxyBypassType* bypass_type) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -190,15 +185,15 @@
 
   DCHECK(response_headers);
 
-  if (!config_->WasDataReductionProxyUsed(&request,
-                                          data_reduction_proxy_type_info)) {
+  base::Optional<DataReductionProxyTypeInfo> data_reduction_proxy_type_info =
+      config_->FindConfiguredDataReductionProxy(request.proxy_server());
+  if (!data_reduction_proxy_type_info)
     return false;
-  }
 
   // At this point, the response is expected to have the data reduction proxy
   // via header, so detect and report cases where the via header is missing.
   DataReductionProxyBypassStats::DetectAndRecordMissingViaHeaderResponseCode(
-      data_reduction_proxy_type_info->proxy_index == 0, *response_headers);
+      data_reduction_proxy_type_info->proxy_index == 0U, *response_headers);
 
   // GetDataReductionProxyBypassType will only log a net_log event if a bypass
   // command was sent via the data reduction proxy headers.
@@ -212,16 +207,20 @@
 
   DCHECK(request.context());
   DCHECK(request.context()->proxy_resolution_service());
-  DCHECK_LT(0U, data_reduction_proxy_type_info->proxy_servers.size());
-  net::ProxyServer proxy_server =
-      data_reduction_proxy_type_info->proxy_servers.front();
+  DCHECK_GT(data_reduction_proxy_type_info->proxy_servers.size(),
+            data_reduction_proxy_type_info->proxy_index);
+
+  const net::ProxyServer& proxy_server =
+      data_reduction_proxy_type_info
+          ->proxy_servers[data_reduction_proxy_type_info->proxy_index]
+          .proxy_server();
 
   // Only record UMA if the proxy isn't already on the retry list.
   if (!config_->IsProxyBypassed(
-          request.context()->proxy_resolution_service()->proxy_retry_info(), proxy_server,
-          nullptr)) {
+          request.context()->proxy_resolution_service()->proxy_retry_info(),
+          proxy_server, nullptr)) {
     DataReductionProxyBypassStats::RecordDataReductionProxyBypassInfo(
-        data_reduction_proxy_type_info->proxy_index == 0,
+        data_reduction_proxy_type_info->proxy_index == 0U,
         data_reduction_proxy_info->bypass_all, proxy_server, *bypass_type);
   }
   return true;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
index c0eff9e..af3bd0b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
@@ -16,7 +16,6 @@
 namespace data_reduction_proxy {
 
 class DataReductionProxyConfig;
-struct DataReductionProxyTypeInfo;
 
 // Class responsible for determining when a response should or should not cause
 // the data reduction proxy to be bypassed, and to what degree. Owned by the
@@ -48,7 +47,6 @@
   bool HandleInvalidResponseHeadersCase(
       const net::URLRequest& request,
       DataReductionProxyInfo* data_reduction_proxy_info,
-      DataReductionProxyTypeInfo* data_reduction_proxy_type_info,
       DataReductionProxyBypassType* bypass_type) const;
 
   // Decides whether to mark the data reduction proxy as temporarily bad and
@@ -59,7 +57,6 @@
       const net::URLRequest& request,
       DataReductionProxyBypassType* proxy_bypass_type,
       DataReductionProxyInfo* data_reduction_proxy_info,
-      DataReductionProxyTypeInfo* data_reduction_proxy_type_info,
       DataReductionProxyBypassType* bypass_type) const;
 
   // Must outlive |this|.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
index 47b69e4..04b5c04 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -355,8 +355,8 @@
 
     std::string response2;
     std::string request2;
-    std::string response2_via_header = "";
-    std::string request2_connection_type = "";
+    std::string response2_via_header;
+    std::string request2_connection_type;
     std::string request2_path = "/";
 
     if (expected_bad_proxy_count == 1) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
index 0c52cf82..1c47b73d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
@@ -11,6 +11,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_server.h"
@@ -119,14 +120,16 @@
     bool started,
     int net_error) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DataReductionProxyTypeInfo proxy_info;
+
+  base::Optional<DataReductionProxyTypeInfo> proxy_info =
+      data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+          request->proxy_server());
+
   // Ignore requests that did not use the data reduction proxy. The check for
   // LOAD_BYPASS_PROXY is necessary because the proxy_server() in the |request|
   // might still be set to the data reduction proxy if |request| was retried
   // over direct and a network error occurred while retrying it.
-  if (!data_reduction_proxy_config_->WasDataReductionProxyUsed(request,
-                                                               &proxy_info) ||
-      (request->load_flags() & net::LOAD_BYPASS_PROXY) != 0 ||
+  if (!proxy_info || (request->load_flags() & net::LOAD_BYPASS_PROXY) != 0 ||
       net_error != net::OK) {
     return;
   }
@@ -136,15 +139,11 @@
   // Report the success counts.
   UMA_HISTOGRAM_COUNTS_100(
       "DataReductionProxy.SuccessfulRequestCompletionCounts",
-      proxy_info.proxy_index);
-
-  DCHECK(request->proxy_server().host_port_pair().Equals(
-      proxy_info.proxy_servers.front().host_port_pair()));
+      proxy_info->proxy_index);
 
   // It is possible that the scheme of request->proxy_server() is different
   // from the scheme of proxy_info.proxy_servers.front(). The former may be set
   // to QUIC by the network stack, while the latter may be set to HTTPS.
-
   UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.ProxySchemeUsed",
                             util::ConvertNetProxySchemeToProxyScheme(
                                 request->proxy_server().scheme()),
@@ -152,8 +151,8 @@
   if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) {
     UMA_HISTOGRAM_COUNTS_100(
         "DataReductionProxy.SuccessfulRequestCompletionCounts.MainFrame",
-        proxy_info.proxy_index);
-    }
+        proxy_info->proxy_index);
+  }
 }
 
 void DataReductionProxyBypassStats::SetBypassType(
@@ -173,35 +172,33 @@
     const net::ProxyServer& bypassed_proxy,
     int net_error) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DataReductionProxyTypeInfo data_reduction_proxy_info;
-  if (bypassed_proxy.is_valid() && !bypassed_proxy.is_direct() &&
-      data_reduction_proxy_config_->IsDataReductionProxy(
-          bypassed_proxy, &data_reduction_proxy_info)) {
-    proxy_net_errors_count_++;
+  if (!bypassed_proxy.is_valid() || bypassed_proxy.is_direct())
+    return;
 
-    // To account for the case when the proxy is reachable for sometime, and
-    // then gets blocked, we reset counts when number of errors exceed
-    // the threshold.
-    if (proxy_net_errors_count_ >= kMaxFailedRequestsBeforeReset &&
-        successful_requests_through_proxy_count_ >
-            kMaxSuccessfulRequestsWhenUnavailable) {
-      ClearRequestCounts();
-    } else {
-      NotifyUnavailabilityIfChanged();
-    }
+  base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+      data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+          bypassed_proxy);
+  if (!proxy_type_info)
+    return;
 
-    if (data_reduction_proxy_info.proxy_index == 0) {
-      RecordDataReductionProxyBypassInfo(
-          true, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR);
-      RecordDataReductionProxyBypassOnNetworkError(
-          true, bypassed_proxy, net_error);
-    } else {
-      RecordDataReductionProxyBypassInfo(
-          false, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR);
-      RecordDataReductionProxyBypassOnNetworkError(
-          false, bypassed_proxy, net_error);
-    }
+  proxy_net_errors_count_++;
+
+  // To account for the case when the proxy is reachable for sometime, and
+  // then gets blocked, we reset counts when number of errors exceed
+  // the threshold.
+  if (proxy_net_errors_count_ >= kMaxFailedRequestsBeforeReset &&
+      successful_requests_through_proxy_count_ >
+          kMaxSuccessfulRequestsWhenUnavailable) {
+    ClearRequestCounts();
+  } else {
+    NotifyUnavailabilityIfChanged();
   }
+
+  const bool is_first_proxy = proxy_type_info->proxy_index == 0U;
+  RecordDataReductionProxyBypassInfo(is_first_proxy, false, bypassed_proxy,
+                                     BYPASS_EVENT_TYPE_NETWORK_ERROR);
+  RecordDataReductionProxyBypassOnNetworkError(is_first_proxy, bypassed_proxy,
+                                               net_error);
 }
 
 void DataReductionProxyBypassStats::ClearRequestCounts() {
@@ -237,9 +234,8 @@
   if (!request.url().SchemeIsHTTPOrHTTPS())
     return;
 
-  DataReductionProxyTypeInfo data_reduction_proxy_type_info;
-  if (data_reduction_proxy_config_->WasDataReductionProxyUsed(
-          &request, &data_reduction_proxy_type_info)) {
+  if (data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+          request.proxy_server())) {
     RecordBypassedBytes(last_bypass_type_,
                         DataReductionProxyBypassStats::NOT_BYPASSED,
                         content_length);
@@ -256,7 +252,6 @@
   // Now that the data reduction proxy is a best effort proxy, if the effective
   // proxy configuration resolves to anything other than direct:// for a URL,
   // the data reduction proxy will not be used.
-  DCHECK(data_reduction_proxy_type_info.proxy_servers.empty());
   if (!request.proxy_server().is_valid() ||
       !request.proxy_server().is_direct()) {
     RecordBypassedBytes(last_bypass_type_,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
index 549cc91d..338c3a8 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
@@ -40,6 +40,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
+#include "net/log/test_net_log.h"
 #include "net/socket/socket_test_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
@@ -375,38 +376,6 @@
 
   net::TestDelegate* delegate() { return &delegate_; }
 
-  // Marks a data reduction proxy as bypassed if
-  // |bypassed_proxy_server_is_data_reduction_proxy| is true. Then, runs a
-  // request via data reduction proxy if |is_data_reduction_proxy| is true,
-  // and verifies that proxy is unreachable only if |is_unreachable| is true.
-  void VerifyProxyReachablity(
-      bool bypassed_proxy_server_is_data_reduction_proxy,
-      bool is_data_reduction_proxy,
-      bool is_unreachable) {
-    InitializeContext();
-
-    std::string proxy = bypassed_proxy_server_is_data_reduction_proxy
-                            ? "origin.net:80"
-                            : "foo.net:80";
-    net::ProxyServer fallback_proxy_server =
-        net::ProxyServer::FromURI(proxy, net::ProxyServer::SCHEME_HTTP);
-
-    bypass_stats()->OnProxyFallback(fallback_proxy_server,
-                                    net::ERR_PROXY_CONNECTION_FAILED);
-    drp_test_context()->RunUntilIdle();
-
-    if (!is_data_reduction_proxy)
-      config()->SetWasDataReductionProxyNotUsed();
-
-    CreateAndExecuteRequest(GURL("http://bar.com"), net::LOAD_NORMAL, net::OK,
-                            "HTTP/1.1 200 OK\r\n"
-                            "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
-                            kNextBody.c_str(), nullptr, nullptr);
-
-    drp_test_context()->RunUntilIdle();
-    EXPECT_EQ(is_unreachable, IsUnreachable());
-  }
-
  private:
   base::MessageLoopForIO message_loop_;
   net::TestDelegate delegate_;
@@ -746,26 +715,6 @@
 }
 
 TEST_F(DataReductionProxyBypassStatsEndToEndTest,
-       IsDataReductionProxyUnreachable_Unreachable) {
-  VerifyProxyReachablity(true, false, true);
-}
-
-TEST_F(DataReductionProxyBypassStatsEndToEndTest,
-       IsDataReductionProxyUnreachable_Unreachable_Then_Reachable) {
-  VerifyProxyReachablity(true, true, false);
-}
-
-TEST_F(DataReductionProxyBypassStatsEndToEndTest,
-       IsDataReductionProxyUnreachable_Not_A_data_reduction_proxy_1) {
-  VerifyProxyReachablity(false, true, false);
-}
-
-TEST_F(DataReductionProxyBypassStatsEndToEndTest,
-       IsDataReductionProxyUnreachableNot_A_data_reduction_proxy_2) {
-  VerifyProxyReachablity(false, false, false);
-}
-
-TEST_F(DataReductionProxyBypassStatsEndToEndTest,
        ProxyUnreachableThenReachable) {
   net::ProxyServer fallback_proxy_server =
       net::ProxyServer::FromURI("origin.net:80", net::ProxyServer::SCHEME_HTTP);
@@ -867,72 +816,124 @@
   }
 }
 
-TEST_F(DataReductionProxyBypassStatsEndToEndTest, SuccessfulRequestCompletion) {
-  const std::string kPrimaryHistogramName =
+TEST_F(DataReductionProxyBypassStatsEndToEndTest,
+       SuccessfulPrimaryProxyRequestCompletion) {
+  const std::string kHistogramName =
       "DataReductionProxy.SuccessfulRequestCompletionCounts";
-  const std::string kPrimaryMainFrameHistogramName =
+  const std::string kMainFrameHistogramName =
       "DataReductionProxy.SuccessfulRequestCompletionCounts.MainFrame";
 
   InitializeContext();
+
   const struct {
-    bool was_proxy_used;
-    bool is_load_bypass_proxy;
-    size_t proxy_index;
-    bool is_main_frame;
+    int load_flags;
     net::Error net_error;
-  } tests[] = {{false, true, 0, true, net::OK},
-               {false, true, 0, false, net::ERR_TOO_MANY_REDIRECTS},
-               {false, false, 0, true, net::OK},
-               {false, false, 0, false, net::ERR_TOO_MANY_REDIRECTS},
-               {true, false, 0, true, net::OK},
-               {true, false, 0, true, net::ERR_TOO_MANY_REDIRECTS},
-               {true, false, 0, false, net::OK},
-               {true, false, 0, false, net::ERR_TOO_MANY_REDIRECTS},
-               {true, false, 1, true, net::OK},
-               {true, false, 1, true, net::ERR_TOO_MANY_REDIRECTS},
-               {true, false, 1, false, net::OK},
-               {true, false, 1, false, net::ERR_TOO_MANY_REDIRECTS}};
+    bool expect_histogram_sample;
+    bool expect_main_frame_histogram_sample;
+  } tests[] = {
+      {net::LOAD_BYPASS_PROXY | net::LOAD_MAIN_FRAME_DEPRECATED, net::OK, false,
+       false},
+      {net::LOAD_BYPASS_PROXY | net::LOAD_MAIN_FRAME_DEPRECATED,
+       net::ERR_TOO_MANY_REDIRECTS, false, false},
+      {net::LOAD_BYPASS_PROXY, net::OK, false, false},
+      {net::LOAD_BYPASS_PROXY, net::ERR_TOO_MANY_REDIRECTS, false, false},
+      {net::LOAD_MAIN_FRAME_DEPRECATED, net::OK, true, true},
+      {net::LOAD_MAIN_FRAME_DEPRECATED, net::ERR_TOO_MANY_REDIRECTS, false,
+       false},
+      {0, net::OK, true, false},
+      {0, net::ERR_TOO_MANY_REDIRECTS, false, false},
+  };
 
   for (const auto& test : tests) {
-    config()->ResetWasDataReductionProxyUsed();
     base::HistogramTester histogram_tester;
-
-    // Proxy succeeds.
-    int load_flags = net::LOAD_NORMAL;
-    if (test.is_load_bypass_proxy) {
-      load_flags |= net::LOAD_BYPASS_PROXY;
-    }
-    if (test.is_main_frame) {
-      load_flags |= net::LOAD_MAIN_FRAME_DEPRECATED;
-    }
-
-    if (!test.was_proxy_used)
-      config()->SetWasDataReductionProxyNotUsed();
-    else {
-      config()->SetWasDataReductionProxyUsedProxyIndex(test.proxy_index);
-    }
-
-    CreateAndExecuteRequest(GURL("http://bar.com"), load_flags, test.net_error,
+    CreateAndExecuteRequest(GURL("http://foo.com"), test.load_flags,
+                            test.net_error,
                             "HTTP/1.1 200 OK\r\n"
                             "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
                             kNextBody.c_str(), nullptr, nullptr);
     drp_test_context()->RunUntilIdle();
 
-    if (test.was_proxy_used && !test.is_load_bypass_proxy &&
-        test.net_error == net::OK) {
-      histogram_tester.ExpectUniqueSample(kPrimaryHistogramName,
-                                          test.proxy_index, 1);
-    } else {
-      histogram_tester.ExpectTotalCount(kPrimaryHistogramName, 0);
-    }
+    if (test.expect_histogram_sample)
+      histogram_tester.ExpectUniqueSample(kHistogramName, 0, 1);
+    else
+      histogram_tester.ExpectTotalCount(kHistogramName, 0);
 
-    if (test.was_proxy_used && !test.is_load_bypass_proxy &&
-        test.is_main_frame && test.net_error == net::OK) {
-      histogram_tester.ExpectUniqueSample(kPrimaryMainFrameHistogramName,
-                                          test.proxy_index, 1);
-    } else {
-      histogram_tester.ExpectTotalCount(kPrimaryMainFrameHistogramName, 0);
-    }
+    if (test.expect_main_frame_histogram_sample)
+      histogram_tester.ExpectUniqueSample(kMainFrameHistogramName, 0, 1);
+    else
+      histogram_tester.ExpectTotalCount(kMainFrameHistogramName, 0);
+  }
+}
+
+TEST_F(DataReductionProxyBypassStatsEndToEndTest,
+       SuccessfulFallbackProxyRequestCompletion) {
+  const std::string kHistogramName =
+      "DataReductionProxy.SuccessfulRequestCompletionCounts";
+  const std::string kMainFrameHistogramName =
+      "DataReductionProxy.SuccessfulRequestCompletionCounts.MainFrame";
+
+  // Explicitly set primary and fallback Data Reduction Proxies to use.
+  config()->test_params()->SetProxiesForHttp(
+      std::vector<DataReductionProxyServer>(
+          {DataReductionProxyServer(
+               net::ProxyServer::FromURI("http://origin.net",
+                                         net::ProxyServer::SCHEME_HTTP),
+               ProxyServer::CORE),
+           DataReductionProxyServer(
+               net::ProxyServer::FromURI("http://fallback.net",
+                                         net::ProxyServer::SCHEME_HTTP),
+               ProxyServer::CORE)}));
+
+  // Make the first Data Reduction Proxy host in the list of Data Reduction
+  // Proxies to use fail to resolve, so that the tests below will use the
+  // fallback proxy.
+  std::unique_ptr<net::MockHostResolver> host_resolver(
+      new net::MockHostResolver());
+  const DataReductionProxyServer& primary_proxy =
+      config()->test_params()->proxies_for_http().front();
+  host_resolver->rules()->AddSimulatedFailure(
+      primary_proxy.proxy_server().host_port_pair().host());
+
+  set_host_resolver(host_resolver.get());
+  InitializeContext();
+
+  const struct {
+    int load_flags;
+    net::Error net_error;
+    bool expect_histogram_sample;
+    bool expect_main_frame_histogram_sample;
+  } tests[] = {
+      {net::LOAD_BYPASS_PROXY | net::LOAD_MAIN_FRAME_DEPRECATED, net::OK, false,
+       false},
+      {net::LOAD_BYPASS_PROXY | net::LOAD_MAIN_FRAME_DEPRECATED,
+       net::ERR_TOO_MANY_REDIRECTS, false, false},
+      {net::LOAD_BYPASS_PROXY, net::OK, false, false},
+      {net::LOAD_BYPASS_PROXY, net::ERR_TOO_MANY_REDIRECTS, false, false},
+      {net::LOAD_MAIN_FRAME_DEPRECATED, net::OK, true, true},
+      {net::LOAD_MAIN_FRAME_DEPRECATED, net::ERR_TOO_MANY_REDIRECTS, false,
+       false},
+      {0, net::OK, true, false},
+      {0, net::ERR_TOO_MANY_REDIRECTS, false, false},
+  };
+
+  for (const auto& test : tests) {
+    base::HistogramTester histogram_tester;
+    CreateAndExecuteRequest(GURL("http://foo.com"), test.load_flags,
+                            test.net_error,
+                            "HTTP/1.1 200 OK\r\n"
+                            "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+                            kNextBody.c_str(), nullptr, nullptr);
+    drp_test_context()->RunUntilIdle();
+
+    if (test.expect_histogram_sample)
+      histogram_tester.ExpectUniqueSample(kHistogramName, 1, 1);
+    else
+      histogram_tester.ExpectTotalCount(kHistogramName, 0);
+
+    if (test.expect_main_frame_histogram_sample)
+      histogram_tester.ExpectUniqueSample(kMainFrameHistogramName, 1, 1);
+    else
+      histogram_tester.ExpectTotalCount(kMainFrameHistogramName, 0);
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index dd6402d..4d12e6c6 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -6,7 +6,6 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -33,6 +32,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/previews/core/previews_decider.h"
 #include "components/variations/variations_associated_data.h"
@@ -247,73 +247,11 @@
   }
 }
 
-bool DataReductionProxyConfig::WasDataReductionProxyUsed(
-    const net::URLRequest* request,
-    DataReductionProxyTypeInfo* proxy_info) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(request);
-  return IsDataReductionProxy(request->proxy_server(), proxy_info);
-}
-
-bool DataReductionProxyConfig::IsDataReductionProxyServerCore(
+base::Optional<DataReductionProxyTypeInfo>
+DataReductionProxyConfig::FindConfiguredDataReductionProxy(
     const net::ProxyServer& proxy_server) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsDataReductionProxy(proxy_server, nullptr /* proxy_info */));
-
-  const net::HostPortPair& host_port_pair = proxy_server.host_port_pair();
-
-  const std::vector<DataReductionProxyServer>& data_reduction_proxy_servers =
-      config_values_->proxies_for_http();
-
-  const auto proxy_it = std::find_if(
-      data_reduction_proxy_servers.begin(), data_reduction_proxy_servers.end(),
-      [&host_port_pair](const DataReductionProxyServer& proxy) {
-        return proxy.proxy_server().is_valid() &&
-               proxy.proxy_server().host_port_pair().Equals(host_port_pair);
-      });
-
-  return proxy_it->IsCoreProxy();
-}
-
-bool DataReductionProxyConfig::IsDataReductionProxy(
-    const net::ProxyServer& proxy_server,
-    DataReductionProxyTypeInfo* proxy_info) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!proxy_server.is_valid() || proxy_server.is_direct())
-    return false;
-
-  // Only compare the host port pair of the |proxy_server| since the proxy
-  // scheme of the stored data reduction proxy may be different than the proxy
-  // scheme of |proxy_server|. This may happen even when the |proxy_server| is a
-  // valid data reduction proxy. As an example, the stored data reduction proxy
-  // may have a proxy scheme of HTTPS while |proxy_server| may have QUIC as the
-  // proxy scheme.
-  const net::HostPortPair& host_port_pair = proxy_server.host_port_pair();
-
-  const std::vector<DataReductionProxyServer>& data_reduction_proxy_servers =
-      config_values_->proxies_for_http();
-
-  const auto proxy_it = std::find_if(
-      data_reduction_proxy_servers.begin(), data_reduction_proxy_servers.end(),
-      [&host_port_pair](const DataReductionProxyServer& proxy) {
-        return proxy.proxy_server().is_valid() &&
-               proxy.proxy_server().host_port_pair().Equals(host_port_pair);
-      });
-
-  if (proxy_it == data_reduction_proxy_servers.end())
-    return false;
-
-  if (!proxy_info)
-    return true;
-
-  proxy_info->proxy_servers =
-      DataReductionProxyServer::ConvertToNetProxyServers(
-          std::vector<DataReductionProxyServer>(
-              proxy_it, data_reduction_proxy_servers.end()));
-  proxy_info->proxy_index =
-      static_cast<size_t>(proxy_it - data_reduction_proxy_servers.begin());
-  return true;
+  return config_values_->FindConfiguredDataReductionProxy(proxy_server);
 }
 
 bool DataReductionProxyConfig::IsBypassedByDataReductionProxyLocalRules(
@@ -329,7 +267,7 @@
     return true;
   if (result.proxy_server().is_direct())
     return true;
-  return !IsDataReductionProxy(result.proxy_server(), nullptr);
+  return !FindConfiguredDataReductionProxy(result.proxy_server());
 }
 
 bool DataReductionProxyConfig::AreDataReductionProxiesBypassed(
@@ -374,7 +312,7 @@
       continue;
 
     base::TimeDelta delay;
-    if (IsDataReductionProxy(proxy, nullptr)) {
+    if (FindConfiguredDataReductionProxy(proxy)) {
       if (!IsProxyBypassed(retry_map, proxy, &delay))
         return false;
       if (delay < min_delay)
@@ -418,7 +356,7 @@
       proxy_rules.MapUrlSchemeToProxyList("http");
   if (http_proxy_list && !http_proxy_list->IsEmpty() &&
       // Sufficient to check only the first proxy.
-      IsDataReductionProxy(http_proxy_list->Get(), nullptr)) {
+      FindConfiguredDataReductionProxy(http_proxy_list->Get())) {
     return true;
   }
 
@@ -501,7 +439,7 @@
   const std::vector<DataReductionProxyServer>& proxies =
       DataReductionProxyConfig::GetProxiesForHttp();
 
-  for (const auto proxy_server : proxies) {
+  for (const DataReductionProxyServer& proxy_server : proxies) {
     // First find a proxy server that has never been probed before. Proxies that
     // have been probed before successfully do not need to be probed. On the
     // other hand, proxies that have been probed before unsuccessfully are
@@ -516,7 +454,7 @@
     }
   }
 
-  for (const auto proxy_server : proxies) {
+  for (const DataReductionProxyServer& proxy_server : proxies) {
     // Now find any proxy server that can be probed. This would return proxies
     // that were probed before, the result was unsuccessful, but they have not
     // yet hit the maximum probe retry limit.
@@ -544,9 +482,11 @@
       (success_response == WarmupURLFetcher::FetchResult::kTimedOut &&
        !proxy_server.is_valid());
 
+  base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+      FindConfiguredDataReductionProxy(proxy_server);
+
   // Check the proxy server used.
-  if (!timed_out_with_no_proxy_data &&
-      !IsDataReductionProxy(proxy_server, nullptr)) {
+  if (!timed_out_with_no_proxy_data && !proxy_type_info) {
     // No need to do anything here since the warmup fetch did not go through
     // the data saver proxy.
     return;
@@ -557,7 +497,11 @@
 
   if (!timed_out_with_no_proxy_data) {
     is_secure_proxy = proxy_server.is_https() || proxy_server.is_quic();
-    is_core_proxy = IsDataReductionProxyServerCore(proxy_server);
+
+    DCHECK(proxy_type_info);
+    is_core_proxy = proxy_type_info->proxy_servers[proxy_type_info->proxy_index]
+                        .IsCoreProxy();
+
     // The proxy server through which the warmup URL was fetched should match
     // the proxy server for which the warmup URL is in-flight.
     DCHECK(GetInFlightWarmupProxyDetails());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 986045b..46ad672 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -16,11 +16,13 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/browser/secure_proxy_checker.h"
 #include "components/data_reduction_proxy/core/browser/warmup_url_fetcher.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/previews/core/previews_experiments.h"
 #include "net/base/network_change_notifier.h"
@@ -119,27 +121,11 @@
   // InitDataReductionProxySettings.
   void SetProxyConfig(bool enabled, bool at_startup);
 
-  // Returns true if a Data Reduction Proxy was used for the given |request|.
-  // If true, |proxy_info.proxy_servers.front()| will contain the name of the
-  // proxy that was used. Subsequent entries in |proxy_info.proxy_servers| will
-  // contain the names of the Data Reduction Proxy servers that would be used if
-  // |proxy_info.proxy_servers.front()| is bypassed, if any exist. In addition,
-  // |proxy_info| will note if the proxy used was a fallback. |proxy_info| can
-  // be NULL if the caller isn't interested in its values. Virtualized for
-  // testing.
-  virtual bool WasDataReductionProxyUsed(
-      const net::URLRequest* request,
-      DataReductionProxyTypeInfo* proxy_info) const;
-
-  // Returns true if the specified |proxy_server| matches a Data Reduction
-  // Proxy. If true, |proxy_info.proxy_servers.front()| will contain the name of
-  // the proxy that matches. Subsequent entries in |proxy_info.proxy_servers|
-  // will contain the name of the Data Reduction Proxy servers that would be
-  // used if |proxy_info.proxy_servers.front()| is bypassed, if any exist. In
-  // addition, |proxy_info| will note if the proxy was a fallback. |proxy_info|
-  // can be NULL if the caller isn't interested in its values.
-  bool IsDataReductionProxy(const net::ProxyServer& proxy_server,
-                            DataReductionProxyTypeInfo* proxy_info) const;
+  // If the specified |proxy_server| matches a Data Reduction Proxy, returns the
+  // DataReductionProxyTypeInfo showing where that proxy is in the list of
+  // configured proxies, otherwise returns an empty optional value.
+  base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
+      const net::ProxyServer& proxy_server) const;
 
   // Returns true if this request would be bypassed by the Data Reduction Proxy
   // based on applying the |data_reduction_proxy_config| param rules to the
@@ -327,12 +313,6 @@
   // Fetches the warmup URL.
   void FetchWarmupProbeURL();
 
-  // Returns true if |proxy_server| is a core data reduction proxy server.
-  // Should be called only if |proxy_server| is a valid data reduction proxy
-  // server.
-  bool IsDataReductionProxyServerCore(
-      const net::ProxyServer& proxy_server) const;
-
   // URL fetcher used for performing the secure proxy check.
   std::unique_ptr<SecureProxyChecker> secure_proxy_checker_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 2e430008..1706aa7 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -275,69 +275,66 @@
     const net::LoadTimingInfo& load_timing_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(response_headers);
-  if (config_->IsDataReductionProxy(proxy_server, nullptr)) {
-    if (response_headers->response_code() ==
-        net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
-      std::string session_key =
-          request_options_->GetSessionKeyFromRequestHeaders(request_headers);
 
-      std::string current_session_key = request_options_->GetSecureSession();
+  if (!config_->FindConfiguredDataReductionProxy(proxy_server))
+    return false;
 
-      // If the session key used in the request is different from the current
-      // session key, then the current session key does not need to be
-      // invalidated.
-      if (session_key != current_session_key) {
-        RecordAuthExpiredSessionKey(false);
-        return true;
-      }
-      RecordAuthExpiredSessionKey(true);
-
-      // The default backoff logic is to increment the failure count (and
-      // increase the backoff time) with each response failure to the remote
-      // config service, and to decrement the failure count (and decrease the
-      // backoff time) with each response success. In the case where the
-      // config service returns a success response (decrementing the failure
-      // count) but the session key is continually invalid (as a response from
-      // the Data Reduction Proxy and not the config service), the previous
-      // response should be considered a failure in order to ensure the backoff
-      // time continues to increase.
-      if (previous_request_failed_authentication_)
-        GetBackoffEntry()->InformOfRequest(false);
-
-      // Record that a request resulted in an authentication failure.
-      RecordAuthExpiredHistogram(true);
-      previous_request_failed_authentication_ = true;
-      InvalidateConfig();
-      DCHECK(!config_->IsDataReductionProxy(proxy_server, nullptr));
-
-      if (fetch_in_progress_) {
-        // If a client config fetch is already in progress, then do not start
-        // another fetch since starting a new fetch will cause extra data
-        // usage, and also cancel the ongoing fetch.
-        return true;
-      }
-
-      RetrieveConfig();
-
-      if (!load_timing_info.send_start.is_null() &&
-          !load_timing_info.request_start.is_null() &&
-          net::NetworkChangeNotifier::GetConnectionType() !=
-              net::NetworkChangeNotifier::CONNECTION_NONE &&
-          last_ip_address_change_ < load_timing_info.request_start) {
-        // Record only if there was no change in the IP address since the
-        // request started.
-        UMA_HISTOGRAM_TIMES(
-            "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty",
-            base::TimeTicks::Now() - load_timing_info.request_start);
-      }
-
-      return true;
-    }
-
+  if (response_headers->response_code() !=
+      net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
     previous_request_failed_authentication_ = false;
+    return false;
   }
 
-  return false;
+  // If the session key used in the request is different from the current
+  // session key, then the current session key does not need to be
+  // invalidated.
+  if (request_options_->GetSessionKeyFromRequestHeaders(request_headers) !=
+      request_options_->GetSecureSession()) {
+    RecordAuthExpiredSessionKey(false);
+    return true;
+  }
+  RecordAuthExpiredSessionKey(true);
+
+  // The default backoff logic is to increment the failure count (and
+  // increase the backoff time) with each response failure to the remote
+  // config service, and to decrement the failure count (and decrease the
+  // backoff time) with each response success. In the case where the
+  // config service returns a success response (decrementing the failure
+  // count) but the session key is continually invalid (as a response from
+  // the Data Reduction Proxy and not the config service), the previous
+  // response should be considered a failure in order to ensure the backoff
+  // time continues to increase.
+  if (previous_request_failed_authentication_)
+    GetBackoffEntry()->InformOfRequest(false);
+
+  // Record that a request resulted in an authentication failure.
+  RecordAuthExpiredHistogram(true);
+  previous_request_failed_authentication_ = true;
+  InvalidateConfig();
+  DCHECK(config_->GetProxiesForHttp().empty());
+
+  if (fetch_in_progress_) {
+    // If a client config fetch is already in progress, then do not start
+    // another fetch since starting a new fetch will cause extra data
+    // usage, and also cancel the ongoing fetch.
+    return true;
+  }
+
+  RetrieveConfig();
+
+  if (!load_timing_info.send_start.is_null() &&
+      !load_timing_info.request_start.is_null() &&
+      net::NetworkChangeNotifier::GetConnectionType() !=
+          net::NetworkChangeNotifier::CONNECTION_NONE &&
+      last_ip_address_change_ < load_timing_info.request_start) {
+    // Record only if there was no change in the IP address since the
+    // request started.
+    UMA_HISTOGRAM_TIMES(
+        "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty",
+        base::TimeTicks::Now() - load_timing_info.request_start);
+  }
+
+  return true;
 }
 
 net::BackoffEntry* DataReductionProxyConfigServiceClient::GetBackoffEntry() {
@@ -410,7 +407,7 @@
   request.SerializeToString(&serialized_request);
   std::unique_ptr<net::URLFetcher> fetcher =
       GetURLFetcherForConfig(config_service_url_, serialized_request);
-  if (!fetcher.get()) {
+  if (!fetcher) {
     HandleResponse(std::string(),
                    net::URLRequestStatus::FromError(net::ERR_ABORTED),
                    net::URLFetcher::RESPONSE_CODE_INVALID);
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 0ddeacf..e0834e1 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
@@ -991,7 +991,7 @@
                           std::string(kSuccessSessionKey) + ", key=value");
   // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote
   // config.
-  EXPECT_FALSE(config_client()->ShouldRetryDueToAuthFailure(
+  EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
       request_headers, parsed.get(), origin, load_timing_info));
   EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
   histogram_tester.ExpectBucketCount(
@@ -1014,9 +1014,13 @@
   EXPECT_GE(persisted_config_retrieval_time() + base::TimeDelta::FromMinutes(2),
             base::Time::Now());
 
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester.ExpectBucketCount(
+      "DataReductionProxy.ConfigService.AuthExpiredSessionKey",
+      0 /* AUTH_EXPIRED_SESSION_KEY_MISMATCH */, 1);
+  histogram_tester.ExpectBucketCount(
       "DataReductionProxy.ConfigService.AuthExpiredSessionKey",
       1 /* AUTH_EXPIRED_SESSION_KEY_MATCH */, 1);
+
   histogram_tester.ExpectBucketCount(
       "DataReductionProxy.ConfigService.AuthExpired", false, 2);
   histogram_tester.ExpectBucketCount(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index 50b69ecb..15832edb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -12,6 +12,7 @@
 #include "base/time/tick_clock.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -77,34 +78,6 @@
   return DataReductionProxyConfig::GetTicksNow();
 }
 
-bool TestDataReductionProxyConfig::WasDataReductionProxyUsed(
-    const net::URLRequest* request,
-    DataReductionProxyTypeInfo* proxy_info) const {
-  if (was_data_reduction_proxy_used_ &&
-      !was_data_reduction_proxy_used_.value()) {
-    return false;
-  }
-  bool was_data_reduction_proxy_used =
-      DataReductionProxyConfig::WasDataReductionProxyUsed(request, proxy_info);
-  if (proxy_info && was_data_reduction_proxy_used && proxy_index_)
-    proxy_info->proxy_index = proxy_index_.value();
-  return was_data_reduction_proxy_used;
-}
-
-void TestDataReductionProxyConfig::SetWasDataReductionProxyNotUsed() {
-  was_data_reduction_proxy_used_ = false;
-}
-
-void TestDataReductionProxyConfig::SetWasDataReductionProxyUsedProxyIndex(
-    int proxy_index) {
-  proxy_index_ = proxy_index;
-}
-
-void TestDataReductionProxyConfig::ResetWasDataReductionProxyUsed() {
-  was_data_reduction_proxy_used_.reset();
-  proxy_index_.reset();
-}
-
 void TestDataReductionProxyConfig::SetIsCaptivePortal(bool is_captive_portal) {
   is_captive_portal_ = is_captive_portal;
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index 99cf7f9..3612ad2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -74,21 +74,6 @@
 
   base::TimeTicks GetTicksNow() const override;
 
-  bool WasDataReductionProxyUsed(
-      const net::URLRequest* request,
-      DataReductionProxyTypeInfo* proxy_info) const override;
-
-  // Sets the data reduction proxy as not used. Subsequent calls to
-  // WasDataReductionProxyUsed() would return false.
-  void SetWasDataReductionProxyNotUsed();
-
-  // Sets the proxy index of the data reduction proxy. Subsequent calls to
-  // WasDataReductionProxyUsed are affected.
-  void SetWasDataReductionProxyUsedProxyIndex(int proxy_index);
-
-  // Resets the behavior of WasDataReductionProxyUsed() calls.
-  void ResetWasDataReductionProxyUsed();
-
   // Sets if the captive portal probe has been blocked for the current network.
   void SetIsCaptivePortal(bool is_captive_portal);
 
@@ -132,9 +117,6 @@
 
   base::Optional<size_t> previous_attempt_counts_;
 
-  base::Optional<bool> was_data_reduction_proxy_used_;
-  base::Optional<int> proxy_index_;
-
   base::Optional<std::string> current_network_id_;
 
   base::Optional<std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>>
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 e36f6562..fc1c62c0 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
@@ -41,6 +41,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/test_previews_decider.h"
@@ -771,57 +772,32 @@
   EXPECT_EQ(delay, min_retry_delay);
 }
 
-TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithParams) {
-  const struct {
-    net::ProxyServer proxy_server;
-    bool expected_result;
-    net::ProxyServer expected_first;
-    net::ProxyServer expected_second;
-    bool expected_is_fallback;
-  } tests[] = {
-      {params()->proxies_for_http().front().proxy_server(), true,
-       params()->proxies_for_http().front().proxy_server(),
-       params()->proxies_for_http().at(1).proxy_server(), false},
-      {params()->proxies_for_http().at(1).proxy_server(), true,
-       params()->proxies_for_http().at(1).proxy_server(), net::ProxyServer(),
-       true},
-  };
-  for (size_t i = 0; i < arraysize(tests); ++i) {
-    std::unique_ptr<TestDataReductionProxyParams> params(
-        new TestDataReductionProxyParams());
-    DataReductionProxyTypeInfo proxy_type_info;
-    std::unique_ptr<DataReductionProxyConfig> config(
-        new DataReductionProxyConfig(task_runner(), net_log(),
-                                     std::move(params), configurator(),
-                                     event_creator()));
-    EXPECT_EQ(
-        tests[i].expected_result,
-        config->IsDataReductionProxy(tests[i].proxy_server, &proxy_type_info))
-        << i;
-    EXPECT_EQ(tests[i].expected_first.is_valid(),
-              proxy_type_info.proxy_servers.size() >= 1 &&
-                  proxy_type_info.proxy_servers[0].is_valid())
-        << i;
-    if (proxy_type_info.proxy_servers.size() >= 1 &&
-        proxy_type_info.proxy_servers[0].is_valid()) {
-      EXPECT_EQ(tests[i].expected_first, proxy_type_info.proxy_servers[0]) << i;
-    }
-    EXPECT_EQ(tests[i].expected_second.is_valid(),
-              proxy_type_info.proxy_servers.size() >= 2 &&
-                  proxy_type_info.proxy_servers[1].is_valid())
-        << i;
-    if (proxy_type_info.proxy_servers.size() >= 2 &&
-        proxy_type_info.proxy_servers[1].is_valid()) {
-      EXPECT_EQ(tests[i].expected_second, proxy_type_info.proxy_servers[1])
-          << i;
-    }
+TEST_F(DataReductionProxyConfigTest,
+       FindConfiguredDataReductionProxyWithParams) {
+  std::unique_ptr<TestDataReductionProxyParams> params(
+      new TestDataReductionProxyParams());
 
-    EXPECT_EQ(tests[i].expected_is_fallback, proxy_type_info.proxy_index != 0)
-        << i;
+  const std::vector<DataReductionProxyServer> expected_proxies =
+      params->proxies_for_http();
+  ASSERT_LT(0U, expected_proxies.size());
+
+  DataReductionProxyConfig config(task_runner(), net_log(), std::move(params),
+                                  configurator(), event_creator());
+
+  for (size_t expected_proxy_index = 0U;
+       expected_proxy_index < expected_proxies.size(); ++expected_proxy_index) {
+    base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+        config.FindConfiguredDataReductionProxy(
+            expected_proxies[expected_proxy_index].proxy_server());
+
+    ASSERT_TRUE(proxy_type_info.has_value());
+    EXPECT_EQ(expected_proxies, proxy_type_info->proxy_servers);
+    EXPECT_EQ(expected_proxy_index, proxy_type_info->proxy_index);
   }
 }
 
-TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithMutableConfig) {
+TEST_F(DataReductionProxyConfigTest,
+       FindConfiguredDataReductionProxyWithMutableConfig) {
   std::vector<DataReductionProxyServer> proxies_for_http;
   proxies_for_http.push_back(DataReductionProxyServer(
       net::ProxyServer::FromURI("https://origin.net:443",
@@ -840,65 +816,38 @@
   const struct {
     DataReductionProxyServer proxy_server;
     bool expected_result;
-    std::vector<DataReductionProxyServer> expected_proxies;
     size_t expected_proxy_index;
   } tests[] = {
-      {
-          proxies_for_http[0], true,
-          std::vector<DataReductionProxyServer>(proxies_for_http.begin(),
-                                                proxies_for_http.end()),
-          0,
-      },
-      {
-          proxies_for_http[1], true,
-          std::vector<DataReductionProxyServer>(proxies_for_http.begin() + 1,
-                                                proxies_for_http.end()),
-          1,
-      },
-      {
-          proxies_for_http[2], true,
-          std::vector<DataReductionProxyServer>(proxies_for_http.begin() + 2,
-                                                proxies_for_http.end()),
-          2,
-      },
-      {
-          DataReductionProxyServer(net::ProxyServer(),
-                                   ProxyServer::UNSPECIFIED_TYPE),
-          false, std::vector<DataReductionProxyServer>(), 0,
-      },
-      {
-          DataReductionProxyServer(
-              net::ProxyServer(
-                  net::ProxyServer::SCHEME_HTTPS,
-                  net::HostPortPair::FromString("otherorigin.net:443")),
-              ProxyServer::UNSPECIFIED_TYPE),
-          false, std::vector<DataReductionProxyServer>(), 0,
-      },
-      {
-          // Verifies that when determining if a proxy is a valid data reduction
-          // proxy, only the host port pairs are compared.
-          DataReductionProxyServer(
-              net::ProxyServer::FromURI("origin.net:443",
-                                        net::ProxyServer::SCHEME_QUIC),
-              ProxyServer::UNSPECIFIED_TYPE),
-          true, std::vector<DataReductionProxyServer>(proxies_for_http.begin(),
-                                                      proxies_for_http.end()),
-          0,
-      },
-      {
-          DataReductionProxyServer(
-              net::ProxyServer::FromURI("origin2.net:443",
-                                        net::ProxyServer::SCHEME_HTTPS),
-              ProxyServer::UNSPECIFIED_TYPE),
-          false, std::vector<DataReductionProxyServer>(), 0,
-      },
-      {
-          DataReductionProxyServer(
-              net::ProxyServer::FromURI("origin2.net:443",
-                                        net::ProxyServer::SCHEME_QUIC),
-              ProxyServer::UNSPECIFIED_TYPE),
-          false, std::vector<DataReductionProxyServer>(), 0,
-      },
+      {proxies_for_http[0], true, 0U},
+      {proxies_for_http[1], true, 1U},
+      {proxies_for_http[2], true, 2U},
+      {DataReductionProxyServer(net::ProxyServer(),
+                                ProxyServer::UNSPECIFIED_TYPE),
+       false, 0U},
+      {DataReductionProxyServer(net::ProxyServer(net::ProxyServer::SCHEME_HTTPS,
+                                                 net::HostPortPair::FromString(
+                                                     "otherorigin.net:443")),
+                                ProxyServer::UNSPECIFIED_TYPE),
+       false, 0U},
+
+      // Verifies that when determining if a proxy is a valid data reduction
+      // proxy, only the host port pairs are compared.
+      {DataReductionProxyServer(
+           net::ProxyServer::FromURI("origin.net:443",
+                                     net::ProxyServer::SCHEME_QUIC),
+           ProxyServer::UNSPECIFIED_TYPE),
+       true, 0U},
+
+      {DataReductionProxyServer(
+           net::ProxyServer::FromURI("origin2.net:443",
+                                     net::ProxyServer::SCHEME_HTTPS),
+           ProxyServer::UNSPECIFIED_TYPE),
+       false, 0U},
+      {DataReductionProxyServer(
+           net::ProxyServer::FromURI("origin2.net:443",
+                                     net::ProxyServer::SCHEME_QUIC),
+           ProxyServer::UNSPECIFIED_TYPE),
+       false, 0U},
   };
 
   std::unique_ptr<DataReductionProxyMutableConfigValues> config_values =
@@ -909,14 +858,15 @@
       task_runner(), net_log(), std::move(config_values), configurator(),
       event_creator()));
   for (const auto& test : tests) {
-    DataReductionProxyTypeInfo proxy_type_info;
-    EXPECT_EQ(test.expected_result,
-              config->IsDataReductionProxy(test.proxy_server.proxy_server(),
-                                           &proxy_type_info));
-    EXPECT_EQ(proxy_type_info.proxy_servers,
-              DataReductionProxyServer::ConvertToNetProxyServers(
-                  test.expected_proxies));
-    EXPECT_EQ(test.expected_proxy_index, proxy_type_info.proxy_index);
+    base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+        config->FindConfiguredDataReductionProxy(
+            test.proxy_server.proxy_server());
+    EXPECT_EQ(test.expected_result, proxy_type_info.has_value());
+
+    if (proxy_type_info) {
+      EXPECT_EQ(proxies_for_http, proxy_type_info->proxy_servers);
+      EXPECT_EQ(test.expected_proxy_index, proxy_type_info->proxy_index);
+    }
   }
 }
 
@@ -935,8 +885,8 @@
                         net::LOAD_MAIN_FRAME_DEPRECATED);
   std::unique_ptr<previews::TestPreviewsDecider> previews_decider =
       std::make_unique<previews::TestPreviewsDecider>(true);
-  EXPECT_FALSE(test_config()->ShouldAcceptServerPreview(
-      *request.get(), *previews_decider.get()));
+  EXPECT_FALSE(
+      test_config()->ShouldAcceptServerPreview(*request, *previews_decider));
 }
 
 TEST_F(DataReductionProxyConfigTest,
@@ -954,8 +904,8 @@
                         net::LOAD_MAIN_FRAME_DEPRECATED);
   std::unique_ptr<previews::TestPreviewsDecider> previews_decider =
       std::make_unique<previews::TestPreviewsDecider>(true);
-  EXPECT_FALSE(test_config()->ShouldAcceptServerPreview(
-      *request.get(), *previews_decider.get()));
+  EXPECT_FALSE(
+      test_config()->ShouldAcceptServerPreview(*request, *previews_decider));
 }
 
 TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerPreview) {
@@ -979,14 +929,14 @@
       std::make_unique<previews::TestPreviewsDecider>(true);
 
   // Verify true for no flags.
-  EXPECT_TRUE(test_config()->ShouldAcceptServerPreview(
-      *request.get(), *previews_decider.get()));
+  EXPECT_TRUE(
+      test_config()->ShouldAcceptServerPreview(*request, *previews_decider));
 
   // Verify PreviewsDecider check.
   base::CommandLine::ForCurrentProcess()->InitFromArgv(0, nullptr);
   previews_decider = std::make_unique<previews::TestPreviewsDecider>(false);
-  EXPECT_FALSE(test_config()->ShouldAcceptServerPreview(
-      *request.get(), *previews_decider.get()));
+  EXPECT_FALSE(
+      test_config()->ShouldAcceptServerPreview(*request, *previews_decider));
   histogram_tester.ExpectBucketCount(
       "DataReductionProxy.Protocol.NotAcceptingTransform",
       1 /* NOT_ACCEPTING_TRANSFORM_BLACKLISTED */, 1);
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 cdaa80c..f03df0d 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
@@ -94,8 +94,7 @@
       const std::string& expected_bypass_list) {
     test_context_->RunUntilIdle();
     net::ProxyConfig::ProxyRules rules =
-        config_
-            ->CreateProxyConfig(probe_url_config, *manager_.get(), http_proxies)
+        config_->CreateProxyConfig(probe_url_config, *manager_, http_proxies)
             .proxy_rules();
     ASSERT_EQ(expected_rules_type, rules.type);
     if (net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME ==
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
index 826be5e..447b117 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
@@ -83,13 +83,12 @@
   std::unique_ptr<net::URLRequest> fake_request(context->CreateRequest(
       GURL("http://www.google.com"), net::RequestPriority::IDLE, nullptr,
       TRAFFIC_ANNOTATION_FOR_TESTS));
-  DataReductionProxyData* data =
-      DataReductionProxyData::GetData(*fake_request.get());
+  DataReductionProxyData* data = DataReductionProxyData::GetData(*fake_request);
   EXPECT_FALSE(data);
   data =
       DataReductionProxyData::GetDataAndCreateIfNecessary(fake_request.get());
   EXPECT_TRUE(data);
-  data = DataReductionProxyData::GetData(*fake_request.get());
+  data = DataReductionProxyData::GetData(*fake_request);
   EXPECT_TRUE(data);
   DataReductionProxyData* data2 =
       DataReductionProxyData::GetDataAndCreateIfNecessary(fake_request.get());
@@ -150,7 +149,7 @@
       DataReductionProxyData::GetDataAndCreateIfNecessary(fake_request.get());
   EXPECT_TRUE(data);
   DataReductionProxyData::ClearData(fake_request.get());
-  data = DataReductionProxyData::GetData(*fake_request.get());
+  data = DataReductionProxyData::GetData(*fake_request);
   EXPECT_FALSE(data);
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
index 206b89a6..21a0b7b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
@@ -77,7 +77,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(result);
   DCHECK(result->is_empty() || result->is_direct() ||
-         !config_->IsDataReductionProxy(result->proxy_server(), nullptr));
+         !config_->FindConfiguredDataReductionProxy(result->proxy_server()));
 
   if (!params::IsIncludedInQuicFieldTrial())
     RecordQuicProxyStatus(QUIC_PROXY_DISABLED_VIA_FIELD_TRIAL);
@@ -162,7 +162,7 @@
                                             int net_error) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (bad_proxy.is_valid() &&
-      config_->IsDataReductionProxy(bad_proxy, nullptr)) {
+      config_->FindConfiguredDataReductionProxy(bad_proxy)) {
     event_creator_->AddProxyFallbackEvent(net_log_, bad_proxy.ToURI(),
                                           net_error);
   }
@@ -187,7 +187,7 @@
 
   net::ProxyServer resolved_proxy_server = result->proxy_server();
   DCHECK(resolved_proxy_server.is_valid());
-  DCHECK(config_->IsDataReductionProxy(resolved_proxy_server, nullptr));
+  DCHECK(config_->FindConfiguredDataReductionProxy(resolved_proxy_server));
 
   if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS() ||
       url.SchemeIsCryptographic()) {
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 76e8d36b..795fde3 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
@@ -707,7 +707,7 @@
 
     std::unique_ptr<net::URLRequest> request =
         FetchURLRequest(GURL("http://example.com/path/"), nullptr,
-                        test.DrpResponseHeaders.c_str(), 1000);
+                        test.DrpResponseHeaders, 1000);
 
     EXPECT_EQ(request->GetTotalReceivedBytes(),
               total_received_bytes() - baseline_received_bytes);
@@ -777,9 +777,8 @@
     int64_t baseline_received_bytes = total_received_bytes();
     int64_t baseline_original_received_bytes = total_original_received_bytes();
 
-    std::unique_ptr<net::URLRequest> request =
-        FetchURLRequest(GURL("http://example.com/path/"), nullptr,
-                        test.DrpResponseHeaders.c_str(), 0);
+    std::unique_ptr<net::URLRequest> request = FetchURLRequest(
+        GURL("http://example.com/path/"), nullptr, test.DrpResponseHeaders, 0);
 
     EXPECT_EQ(request->GetTotalReceivedBytes(),
               total_received_bytes() - baseline_received_bytes);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 050f4c8..ac85b385 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -216,7 +216,7 @@
                                 network_properties_manager_.get());
   bypass_stats_->InitializeOnIOThread();
   proxy_delegate_->InitializeOnIOThread(this);
-  if (config_client_.get())
+  if (config_client_)
     config_client_->InitializeOnIOThread(url_request_context_getter_);
   if (ui_task_runner_->BelongsToCurrentThread()) {
     service_->SetIOData(weak_factory_.GetWeakPtr());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
index 7342956..a897573 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
@@ -114,7 +114,7 @@
 
   // Check that the SimpleURLRequestContextGetter uses vanilla HTTP.
   net::URLRequestContext* request_context =
-      io_data->basic_url_request_context_getter_.get()->GetURLRequestContext();
+      io_data->basic_url_request_context_getter_->GetURLRequestContext();
   const net::HttpNetworkSession::Params* http_params =
       request_context->GetNetworkSessionParams();
   EXPECT_FALSE(http_params->enable_http2);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
index edf466af..ee1d2e9d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.cc
@@ -20,8 +20,8 @@
 
 DataReductionProxyRequestType GetDataReductionProxyRequestType(
     const net::URLRequest& request,
-    const net::ProxyConfig& data_reduction_proxy_config,
-    const DataReductionProxyConfig& config) {
+    const net::ProxyConfig& proxy_config,
+    const DataReductionProxyConfig& data_reduction_proxy_config) {
   if (request.url().SchemeIs(url::kHttpsScheme))
     return HTTPS;
   if (!request.url().SchemeIs(url::kHttpScheme)) {
@@ -41,9 +41,10 @@
   if ((request.load_flags() & net::LOAD_BYPASS_PROXY) ||
       (request.proxy_server().is_valid() &&
        !request.proxy_server().is_direct() &&
-       !config.IsDataReductionProxy(request.proxy_server(), nullptr)) ||
-      config.IsBypassedByDataReductionProxyLocalRules(
-          request, data_reduction_proxy_config)) {
+       !data_reduction_proxy_config.FindConfiguredDataReductionProxy(
+           request.proxy_server())) ||
+      data_reduction_proxy_config.IsBypassedByDataReductionProxyLocalRules(
+          request, proxy_config)) {
     return SHORT_BYPASS;
   }
 
@@ -53,8 +54,8 @@
   }
 
   base::TimeDelta bypass_delay;
-  if (config.AreDataReductionProxiesBypassed(
-          request, data_reduction_proxy_config, &bypass_delay)) {
+  if (data_reduction_proxy_config.AreDataReductionProxiesBypassed(
+          request, proxy_config, &bypass_delay)) {
     if (bypass_delay > base::TimeDelta::FromSeconds(kLongBypassDelayInSeconds))
       return LONG_BYPASS;
     return SHORT_BYPASS;
@@ -68,7 +69,8 @@
   // if they came through the Data Reduction Proxy.
   if (request.response_headers() &&
       request.response_headers()->response_code() == net::HTTP_NOT_MODIFIED &&
-      config.WasDataReductionProxyUsed(&request, nullptr)) {
+      data_reduction_proxy_config.FindConfiguredDataReductionProxy(
+          request.proxy_server())) {
     return VIA_DATA_REDUCTION_PROXY;
   }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
index 9c52af5..d06dd62 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h
@@ -47,8 +47,8 @@
 // Returns DataReductionProxyRequestType for |request|.
 DataReductionProxyRequestType GetDataReductionProxyRequestType(
     const net::URLRequest& request,
-    const net::ProxyConfig& data_reduction_proxy_config,
-    const DataReductionProxyConfig& config);
+    const net::ProxyConfig& proxy_config,
+    const DataReductionProxyConfig& data_reduction_proxy_config);
 
 }  // namespace data_reduction_proxy
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
index ddd898a4..092520bc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
@@ -4,8 +4,11 @@
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 
+#include <algorithm>
+
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "net/base/proxy_server.h"
 
 namespace data_reduction_proxy {
 
@@ -34,15 +37,58 @@
   return proxies_for_http_;
 }
 
-void DataReductionProxyMutableConfigValues::UpdateValues(
-    const std::vector<DataReductionProxyServer>& proxies_for_http) {
+base::Optional<DataReductionProxyTypeInfo>
+DataReductionProxyMutableConfigValues::FindConfiguredDataReductionProxy(
+    const net::ProxyServer& proxy_server) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  proxies_for_http_ = proxies_for_http;
+
+  base::Optional<DataReductionProxyTypeInfo> info =
+      DataReductionProxyParams::FindConfiguredProxyInVector(proxies_for_http(),
+                                                            proxy_server);
+  if (info)
+    return info;
+
+  for (const auto& recent_proxies : recently_configured_proxy_lists_) {
+    base::Optional<DataReductionProxyTypeInfo> recent_info =
+        DataReductionProxyParams::FindConfiguredProxyInVector(recent_proxies,
+                                                              proxy_server);
+    if (recent_info)
+      return recent_info;
+  }
+  return base::nullopt;
+}
+
+void DataReductionProxyMutableConfigValues::UpdateValues(
+    const std::vector<DataReductionProxyServer>& new_proxies_for_http) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  std::vector<DataReductionProxyServer> previous_proxies = proxies_for_http();
+
+  proxies_for_http_.clear();
+  std::remove_copy_if(new_proxies_for_http.begin(), new_proxies_for_http.end(),
+                      std::back_inserter(proxies_for_http_),
+                      [](const DataReductionProxyServer& proxy) {
+                        return !proxy.proxy_server().is_valid() ||
+                               proxy.proxy_server().is_direct();
+                      });
+
+  if (previous_proxies.empty() || proxies_for_http() == previous_proxies) {
+    // There's no point in keeping track of an empty recent list of proxies or a
+    // list of proxies that's identical to the currently configured list.
+    return;
+  }
+
+  // Push |previous_proxies| onto the front of the
+  // |recently_configured_proxy_lists_|.
+  std::move_backward(std::begin(recently_configured_proxy_lists_),
+                     std::end(recently_configured_proxy_lists_) - 1,
+                     std::end(recently_configured_proxy_lists_));
+  recently_configured_proxy_lists_[0] = std::move(previous_proxies);
 }
 
 void DataReductionProxyMutableConfigValues::Invalidate() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  proxies_for_http_.clear();
+  UpdateValues(std::vector<DataReductionProxyServer>());
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
index 145db2b..ec52878f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
@@ -8,10 +8,14 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
-#include "net/base/proxy_server.h"
-#include "url/gurl.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+
+namespace net {
+class ProxyServer;
+}
 
 namespace data_reduction_proxy {
 
@@ -26,16 +30,17 @@
   ~DataReductionProxyMutableConfigValues() override;
 
   // Updates |proxies_for_http_| with the provided values.
-  // Virtual for testing.
-  virtual void UpdateValues(
-      const std::vector<DataReductionProxyServer>& proxies_for_http);
+  void UpdateValues(
+      const std::vector<DataReductionProxyServer>& new_proxies_for_http);
 
   // Invalidates |this| by clearing the stored Data Reduction Proxy servers.
   void Invalidate();
 
-  // Overrides of |DataReductionProxyConfigValues|
+  // Overrides of |DataReductionProxyConfigValues|.
   const std::vector<DataReductionProxyServer>& proxies_for_http()
       const override;
+  base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
+      const net::ProxyServer& proxy_server) const override;
 
  private:
   std::vector<DataReductionProxyServer> proxies_for_http_;
@@ -45,6 +50,16 @@
   // ones specified from the Data Saver API.
   const bool use_override_proxies_for_http_;
 
+  // Keep track of some of the most recently configured proxy lists. This means
+  // that any requests that were in-progress to a Data Reduction Proxy when the
+  // list of proxies to use is changed can still be recognized as using a Data
+  // Reduction Proxy for the purposes of applying bypass logic and recording
+  // metrics. The number 2 is used because that way the proxy server used by an
+  // in-progress request can still be recognized if both the list of proxy
+  // servers to use changes and the proxy config gets invalidated before that
+  // request's response is processed.
+  std::vector<DataReductionProxyServer> recently_configured_proxy_lists_[2];
+
   // Enforce usage on the IO thread.
   base::ThreadChecker thread_checker_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
index bfea492..61b6ef8 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
@@ -8,9 +8,11 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/optional.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "net/base/proxy_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,6 +21,15 @@
 
 namespace {
 
+void ExpectTypeInfo(
+    base::Optional<DataReductionProxyTypeInfo> type_info,
+    const std::vector<DataReductionProxyServer>& expected_proxy_servers,
+    size_t expected_proxy_index) {
+  ASSERT_TRUE(type_info);
+  EXPECT_EQ(expected_proxy_servers, type_info->proxy_servers);
+  EXPECT_EQ(expected_proxy_index, type_info->proxy_index);
+}
+
 class DataReductionProxyMutableConfigValuesTest : public testing::Test {
  public:
   DataReductionProxyMutableConfigValuesTest() {}
@@ -54,12 +65,34 @@
   proxies_for_http.push_back(DataReductionProxyServer(
       second_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
 
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      first_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      second_proxy_server));
+
   mutable_config_values()->UpdateValues(proxies_for_http);
   EXPECT_EQ(proxies_for_http, mutable_config_values()->proxies_for_http());
 
+  // The configured proxies should be recognized as Data Reduction Proxies.
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     first_proxy_server),
+                 proxies_for_http, 0U);
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     second_proxy_server),
+                 proxies_for_http, 1U);
+
   // Invalidation must clear out the list of proxies and their properties.
   mutable_config_values()->Invalidate();
   EXPECT_TRUE(mutable_config_values()->proxies_for_http().empty());
+
+  // The previously configured proxies should still be recognized as Data
+  // Reduction Proxies, even though the config was invalidated.
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     first_proxy_server),
+                 proxies_for_http, 0U);
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     second_proxy_server),
+                 proxies_for_http, 1U);
 }
 
 // Tests if HTTP proxies are overridden when |kDataReductionProxyHttpProxies|
@@ -70,18 +103,32 @@
       "http://override-first.net;http://override-second.net");
   Init();
 
-  EXPECT_EQ(std::vector<DataReductionProxyServer>(),
-            mutable_config_values()->proxies_for_http());
-
-  std::vector<DataReductionProxyServer> proxies_for_http;
+  net::ProxyServer first_override_proxy_server = net::ProxyServer::FromURI(
+      "http://override-first.net", net::ProxyServer::SCHEME_HTTP);
+  net::ProxyServer second_override_proxy_server = net::ProxyServer::FromURI(
+      "http://override-second.net", net::ProxyServer::SCHEME_HTTP);
 
   net::ProxyServer first_proxy_server(net::ProxyServer::FromURI(
       "http://first.net", net::ProxyServer::SCHEME_HTTP));
-  proxies_for_http.push_back(
-      DataReductionProxyServer(first_proxy_server, ProxyServer::CORE));
-
   net::ProxyServer second_proxy_server = net::ProxyServer::FromURI(
       "http://second.net", net::ProxyServer::SCHEME_HTTP);
+
+  EXPECT_EQ(std::vector<DataReductionProxyServer>(),
+            mutable_config_values()->proxies_for_http());
+
+  // No proxy servers should be recognized as Data Reduction Proxies.
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      first_override_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      second_override_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      first_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      second_proxy_server));
+
+  std::vector<DataReductionProxyServer> proxies_for_http;
+  proxies_for_http.push_back(
+      DataReductionProxyServer(first_proxy_server, ProxyServer::CORE));
   proxies_for_http.push_back(DataReductionProxyServer(
       second_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
 
@@ -89,20 +136,48 @@
 
   std::vector<DataReductionProxyServer> expected_override_proxies_for_http;
   expected_override_proxies_for_http.push_back(DataReductionProxyServer(
-      net::ProxyServer::FromURI("http://override-first.net",
-                                net::ProxyServer::SCHEME_HTTP),
-      ProxyServer::UNSPECIFIED_TYPE));
+      first_override_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
   expected_override_proxies_for_http.push_back(DataReductionProxyServer(
-      net::ProxyServer::FromURI("http://override-second.net",
-                                net::ProxyServer::SCHEME_HTTP),
-      ProxyServer::UNSPECIFIED_TYPE));
+      second_override_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
 
   EXPECT_EQ(expected_override_proxies_for_http,
             mutable_config_values()->proxies_for_http());
 
+  // The overriding proxy servers should be recognized as Data Reduction
+  // Proxies.
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     first_override_proxy_server),
+                 expected_override_proxies_for_http, 0U);
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     second_override_proxy_server),
+                 expected_override_proxies_for_http, 1U);
+
+  // The proxy servers that were overriden should not be recognized as Data
+  // Reduction Proxies.
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      first_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      second_proxy_server));
+
   // Invalidation must clear out the list of proxies and their properties.
   mutable_config_values()->Invalidate();
   EXPECT_TRUE(mutable_config_values()->proxies_for_http().empty());
+
+  // The overriding proxy servers should be recognized as Data Reduction
+  // Proxies.
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     first_override_proxy_server),
+                 expected_override_proxies_for_http, 0U);
+  ExpectTypeInfo(mutable_config_values()->FindConfiguredDataReductionProxy(
+                     second_override_proxy_server),
+                 expected_override_proxies_for_http, 1U);
+
+  // The proxy servers that were overriden should not be recognized as Data
+  // Reduction Proxies.
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      first_proxy_server));
+  EXPECT_FALSE(mutable_config_values()->FindConfiguredDataReductionProxy(
+      second_proxy_server));
 }
 
 // Tests if HTTP proxies are overridden when |kDataReductionProxy| or
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index 2aca3f16..da6a0a7eb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -359,8 +359,8 @@
     using_data_reduction_proxy = false;
   } else if (proxy_info.proxy_server().host_port_pair().IsEmpty()) {
     using_data_reduction_proxy = false;
-  } else if (!data_reduction_proxy_config_->IsDataReductionProxy(
-                 proxy_info.proxy_server(), nullptr)) {
+  } else if (!data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+                 proxy_info.proxy_server())) {
     using_data_reduction_proxy = false;
   }
 
@@ -636,8 +636,8 @@
     const net::ProxyRetryInfoMap& proxy_retry_info) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(proxy_info.is_empty() || proxy_info.is_direct() ||
-         !data_reduction_proxy_config_->IsDataReductionProxy(
-             proxy_info.proxy_server(), nullptr));
+         !data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+             proxy_info.proxy_server()));
   if (!util::EligibleForDataReductionProxy(proxy_info, request.url(),
                                            request.method())) {
     return false;
@@ -658,8 +658,8 @@
 
   // This method should be called only when the resolved proxy was a data
   // saver proxy.
-  DCHECK(data_reduction_proxy_config_->IsDataReductionProxy(
-      proxy_info.proxy_server(), nullptr));
+  DCHECK(data_reduction_proxy_config_->FindConfiguredDataReductionProxy(
+      proxy_info.proxy_server()));
   DCHECK(request.url().is_valid());
   DCHECK(!request.url().SchemeIsCryptographic());
   DCHECK(request.url().SchemeIsHTTPOrHTTPS());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 6f0ceea..1c30339 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -907,15 +907,15 @@
           GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
       fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED);
       lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview(
-          *fake_request.get(), test_previews_decider));
+          *fake_request, test_previews_decider));
       NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info,
                             proxy_retry_info, &headers);
 
       VerifyHeaders(is_data_reduction_proxy_enabled[i], true, headers);
-      VerifyDataReductionProxyData(
-          *fake_request, is_data_reduction_proxy_enabled[i],
-          config()->ShouldAcceptServerPreview(*fake_request.get(),
-                                              test_previews_decider));
+      VerifyDataReductionProxyData(*fake_request,
+                                   is_data_reduction_proxy_enabled[i],
+                                   config()->ShouldAcceptServerPreview(
+                                       *fake_request, test_previews_decider));
     }
 
     {
@@ -990,13 +990,13 @@
           GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
       fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED);
       lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview(
-          *fake_request.get(), test_previews_decider));
+          *fake_request, test_previews_decider));
       NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info,
                             proxy_retry_info, &headers);
-      VerifyDataReductionProxyData(
-          *fake_request, is_data_reduction_proxy_enabled[i],
-          config()->ShouldAcceptServerPreview(*fake_request.get(),
-                                              test_previews_decider));
+      VerifyDataReductionProxyData(*fake_request,
+                                   is_data_reduction_proxy_enabled[i],
+                                   config()->ShouldAcceptServerPreview(
+                                       *fake_request, test_previews_decider));
     }
   }
 }
@@ -1060,8 +1060,7 @@
         &headers);
     network_delegate()->NotifyBeforeSendHeaders(
         request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-    DataReductionProxyData* data =
-        DataReductionProxyData::GetData(*request.get());
+    DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
     if (!test.used_data_reduction_proxy) {
       EXPECT_FALSE(data);
     } else {
@@ -1120,8 +1119,7 @@
     net::ProxyRetryInfoMap proxy_retry_info;
     network_delegate()->NotifyBeforeSendHeaders(
         request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-    DataReductionProxyData* data =
-        DataReductionProxyData::GetData(*request.get());
+    DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
     if (!test.data_reduction_proxy_enabled || !test.used_direct) {
       EXPECT_FALSE(data);
     } else {
@@ -1166,8 +1164,7 @@
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info,
       &headers_original);
-  DataReductionProxyData* data =
-      DataReductionProxyData::GetData(*request.get());
+  DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
 
   EXPECT_TRUE(data);
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
@@ -1182,7 +1179,7 @@
   // Simulate a redirect even though the same URL is used. Should clear
   // DataReductionProxyData.
   network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
-  data = DataReductionProxyData::GetData(*request.get());
+  data = DataReductionProxyData::GetData(*request);
   EXPECT_FALSE(data && data->used_data_reduction_proxy());
 
   // Call NotifyBeforeSendHeaders again with different proxy info to check that
@@ -1199,7 +1196,7 @@
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info,
       &headers_redirect);
-  data = DataReductionProxyData::GetData(*request.get());
+  data = DataReductionProxyData::GetData(*request);
   EXPECT_FALSE(data);
 }
 
@@ -1443,8 +1440,8 @@
           ssl_socket_data_provider());
     }
 
-    std::string via_header = "";
-    std::string ocl_header = "";
+    std::string via_header;
+    std::string ocl_header;
 
     if (test.proxy_config == USE_INSECURE_PROXY) {
       via_header = "Via: 1.1 Chrome-Compression-Proxy\r\n";
@@ -1811,8 +1808,7 @@
       &headers);
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-  DataReductionProxyData* data =
-      DataReductionProxyData::GetData(*request.get());
+  DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
   EXPECT_TRUE(data_reduction_proxy_info.is_http());
   EXPECT_EQ(++page_id, data->page_id().value());
 
@@ -1828,14 +1824,14 @@
       &headers);
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-  data = DataReductionProxyData::GetData(*request.get());
+  data = DataReductionProxyData::GetData(*request);
   EXPECT_EQ(++page_id, data->page_id().value());
 
   // Verify that redirects are the same page ID.
   network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-  data = DataReductionProxyData::GetData(*request.get());
+  data = DataReductionProxyData::GetData(*request);
   EXPECT_EQ(page_id, data->page_id().value());
 
   network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
@@ -1844,7 +1840,7 @@
   page_id = io_data()->request_options()->GeneratePageId();
   network_delegate()->NotifyBeforeSendHeaders(
       request.get(), data_reduction_proxy_info, proxy_retry_info, &headers);
-  data = DataReductionProxyData::GetData(*request.get());
+  data = DataReductionProxyData::GetData(*request);
   EXPECT_EQ(++page_id, data->page_id().value());
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index 1b9ea074..96f4e58 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -51,7 +51,7 @@
 DataReductionProxySettings::DataReductionProxySettings()
     : unreachable_(false),
       deferred_initialization_(false),
-      data_reduction_proxy_enabled_pref_name_(),
+
       prefs_(nullptr),
       config_(nullptr),
       clock_(base::DefaultClock::GetInstance()) {}
@@ -78,7 +78,7 @@
   DCHECK(prefs);
   DCHECK(io_data);
   DCHECK(io_data->config());
-  DCHECK(data_reduction_proxy_service.get());
+  DCHECK(data_reduction_proxy_service);
   data_reduction_proxy_enabled_pref_name_ =
       data_reduction_proxy_enabled_pref_name;
   prefs_ = prefs;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index aced436..0945cd2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -32,9 +32,7 @@
 
 namespace data_reduction_proxy {
 
-DataReductionProxySettingsTestBase::DataReductionProxySettingsTestBase()
-    : testing::Test() {
-}
+DataReductionProxySettingsTestBase::DataReductionProxySettingsTestBase() {}
 
 DataReductionProxySettingsTestBase::~DataReductionProxySettingsTestBase() {}
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index 32df0a9..4da41fd 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -611,7 +611,7 @@
 void DataReductionProxyTestContext::DestroySettings() {
   // Force destruction of |DBDataOwner|, which lives on DB task runner and is
   // indirectly owned by |settings_|.
-  if (settings_.get()) {
+  if (settings_) {
     settings_.reset();
     storage_delegate_->SetStorageDelegate(nullptr);
     RunUntilIdle();
diff --git a/components/data_reduction_proxy/core/browser/db_data_owner.cc b/components/data_reduction_proxy/core/browser/db_data_owner.cc
index 1273720..dbe78df0 100644
--- a/components/data_reduction_proxy/core/browser/db_data_owner.cc
+++ b/components/data_reduction_proxy/core/browser/db_data_owner.cc
@@ -47,7 +47,7 @@
     std::unique_ptr<DataUsageBucket> current) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
 
-  data_usage_->StoreCurrentDataUsageBucket(*current.get());
+  data_usage_->StoreCurrentDataUsageBucket(*current);
 }
 
 void DBDataOwner::DeleteHistoricalDataUsage() {
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn
index d793083..97bc64a9cb 100644
--- a/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -31,6 +31,7 @@
       "data_reduction_proxy_server.h",
       "data_reduction_proxy_switches.cc",
       "data_reduction_proxy_switches.h",
+      "data_reduction_proxy_type_info.h",
       "lofi_decider.h",
       "lofi_ui_service.h",
       "resource_type_provider.h",
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
index 320b6fe..023d8c8 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
@@ -7,6 +7,13 @@
 
 #include <vector>
 
+#include "base/optional.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+
+namespace net {
+class ProxyServer;
+}
+
 namespace data_reduction_proxy {
 
 class DataReductionProxyServer;
@@ -20,6 +27,20 @@
   // included.
   virtual const std::vector<DataReductionProxyServer>& proxies_for_http()
       const = 0;
+
+  // Determines if the given |proxy_server| matches a currently or recent
+  // previously configured Data Reduction Proxy server, returning information
+  // about where that proxy is in the ordered list of proxies to use. It's up to
+  // the implementation to determine what counts as a recent previously
+  // configured Data Reduction Proxy server, but the idea is to be able to
+  // recognize proxies from requests that use the currently configured
+  // |proxies_for_http()| as well as recognize proxies from requests that are
+  // in-progress when the list of proxy servers to use changes. If
+  // |proxy_server| matches multiple proxies, then the most recent and highest
+  // precedence result is returned.
+  virtual base::Optional<DataReductionProxyTypeInfo>
+  FindConfiguredDataReductionProxy(
+      const net::ProxyServer& proxy_server) const = 0;
 };
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
index e257a700..00ec92cd 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc
@@ -269,7 +269,7 @@
   }
 
   std::string json;
-  base::JSONWriter::Write(*last_bypass.get(), &json);
+  base::JSONWriter::Write(*last_bypass, &json);
   return json;
 }
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc
index aede1168..8f822f3 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc
@@ -284,7 +284,7 @@
 
   bypass_event->Set("params", std::move(bypass_params));
   std::string sanitized_output;
-  base::JSONWriter::Write(*sanitized_event.get(), &sanitized_output);
+  base::JSONWriter::Write(*sanitized_event, &sanitized_output);
   event_store()->AddAndSetLastBypassEvent(std::move(bypass_event), 0);
   EXPECT_EQ(sanitized_output, event_store()->SanitizedLastBypassEvent());
 }
@@ -323,7 +323,7 @@
 
   bypass_event->Set("params", std::move(bypass_params));
   std::string sanitized_output;
-  base::JSONWriter::Write(*sanitized_event.get(), &sanitized_output);
+  base::JSONWriter::Write(*sanitized_event, &sanitized_output);
   event_store()->AddAndSetLastBypassEvent(std::move(bypass_event), 0);
   EXPECT_EQ(sanitized_output, event_store()->SanitizedLastBypassEvent());
 }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 1e3fd9a..97afc0a4 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -4,6 +4,7 @@
 
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 
+#include <algorithm>
 #include <map>
 #include <string>
 #include <vector>
@@ -18,6 +19,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/variations/variations_associated_data.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_status_code.h"
 #include "url/url_constants.h"
@@ -329,14 +331,18 @@
     std::string proxy_overrides =
         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
             switches::kDataReductionProxyHttpProxies);
-    std::vector<std::string> proxy_override_values = base::SplitString(
-        proxy_overrides, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-    for (const std::string& proxy_override : proxy_override_values) {
+
+    for (const auto& proxy_override :
+         base::SplitStringPiece(proxy_overrides, ";", base::TRIM_WHITESPACE,
+                                base::SPLIT_WANT_NONEMPTY)) {
+      net::ProxyServer proxy_server = net::ProxyServer::FromURI(
+          proxy_override, net::ProxyServer::SCHEME_HTTP);
+      DCHECK(proxy_server.is_valid());
+      DCHECK(!proxy_server.is_direct());
+
       // Overriding proxies have type UNSPECIFIED_TYPE.
       override_proxies_for_http->push_back(DataReductionProxyServer(
-          net::ProxyServer::FromURI(proxy_override,
-                                    net::ProxyServer::SCHEME_HTTP),
-          ProxyServer::UNSPECIFIED_TYPE));
+          std::move(proxy_server), ProxyServer::UNSPECIFIED_TYPE));
     }
 
     return true;
@@ -345,6 +351,7 @@
   std::string origin =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kDataReductionProxy);
+
   std::string fallback_origin =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kDataReductionProxyFallback);
@@ -353,17 +360,23 @@
     return false;
 
   override_proxies_for_http->clear();
+
   // Overriding proxies have type UNSPECIFIED_TYPE.
   if (!origin.empty()) {
+    net::ProxyServer primary_proxy =
+        net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP);
+    DCHECK(primary_proxy.is_valid());
+    DCHECK(!primary_proxy.is_direct());
     override_proxies_for_http->push_back(DataReductionProxyServer(
-        net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP),
-        ProxyServer::UNSPECIFIED_TYPE));
+        std::move(primary_proxy), ProxyServer::UNSPECIFIED_TYPE));
   }
   if (!fallback_origin.empty()) {
+    net::ProxyServer fallback_proxy = net::ProxyServer::FromURI(
+        fallback_origin, net::ProxyServer::SCHEME_HTTP);
+    DCHECK(fallback_proxy.is_valid());
+    DCHECK(!fallback_proxy.is_direct());
     override_proxies_for_http->push_back(DataReductionProxyServer(
-        net::ProxyServer::FromURI(fallback_origin,
-                                  net::ProxyServer::SCHEME_HTTP),
-        ProxyServer::UNSPECIFIED_TYPE));
+        std::move(fallback_proxy), ProxyServer::UNSPECIFIED_TYPE));
   }
 
   return true;
@@ -385,13 +398,6 @@
 
 }  // namespace params
 
-DataReductionProxyTypeInfo::DataReductionProxyTypeInfo() : proxy_index(0) {}
-
-DataReductionProxyTypeInfo::DataReductionProxyTypeInfo(
-    const DataReductionProxyTypeInfo& other) = default;
-
-DataReductionProxyTypeInfo::~DataReductionProxyTypeInfo() {}
-
 DataReductionProxyParams::DataReductionProxyParams() {
   bool use_override_proxies_for_http =
       params::GetOverrideProxiesForHttpFromCommandLine(&proxies_for_http_);
@@ -407,12 +413,24 @@
                                   net::ProxyServer::SCHEME_HTTP),
         ProxyServer::CORE));
   }
+
+  DCHECK(std::all_of(proxies_for_http_.begin(), proxies_for_http_.end(),
+                     [](const DataReductionProxyServer& proxy) {
+                       return proxy.proxy_server().is_valid() &&
+                              !proxy.proxy_server().is_direct();
+                     }));
 }
 
 DataReductionProxyParams::~DataReductionProxyParams() {}
 
 void DataReductionProxyParams::SetProxiesForHttpForTesting(
     const std::vector<DataReductionProxyServer>& proxies_for_http) {
+  DCHECK(std::all_of(proxies_for_http.begin(), proxies_for_http.end(),
+                     [](const DataReductionProxyServer& proxy) {
+                       return proxy.proxy_server().is_valid() &&
+                              !proxy.proxy_server().is_direct();
+                     }));
+
   proxies_for_http_ = proxies_for_http;
 }
 
@@ -421,4 +439,38 @@
   return proxies_for_http_;
 }
 
+base::Optional<DataReductionProxyTypeInfo>
+DataReductionProxyParams::FindConfiguredDataReductionProxy(
+    const net::ProxyServer& proxy_server) const {
+  return FindConfiguredProxyInVector(proxies_for_http(), proxy_server);
+}
+
+// static
+base::Optional<DataReductionProxyTypeInfo>
+DataReductionProxyParams::FindConfiguredProxyInVector(
+    const std::vector<DataReductionProxyServer>& proxies,
+    const net::ProxyServer& proxy_server) {
+  if (!proxy_server.is_valid() || proxy_server.is_direct())
+    return base::nullopt;
+
+  // Only compare the host port pair of the |proxy_server| since the proxy
+  // scheme of the stored data reduction proxy may be different than the proxy
+  // scheme of |proxy_server|. This may happen even when the |proxy_server| is a
+  // valid data reduction proxy. As an example, the stored data reduction proxy
+  // may have a proxy scheme of HTTPS while |proxy_server| may have QUIC as the
+  // proxy scheme.
+  const net::HostPortPair& host_port_pair = proxy_server.host_port_pair();
+  auto it = std::find_if(
+      proxies.begin(), proxies.end(),
+      [&host_port_pair](const DataReductionProxyServer& proxy) {
+        return proxy.proxy_server().host_port_pair().Equals(host_port_pair);
+      });
+
+  if (it == proxies.end())
+    return base::nullopt;
+
+  return DataReductionProxyTypeInfo(proxies,
+                                    static_cast<size_t>(it - proxies.begin()));
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index 8053a881..eb43fec 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -10,8 +10,10 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -147,17 +149,6 @@
 
 }  // namespace params
 
-// Contains information about a given proxy server. |proxies_for_http| contains
-// the configured data reduction proxy servers. |proxy_index| notes the index
-// of the data reduction proxy used in the list of all data reduction proxies.
-struct DataReductionProxyTypeInfo {
-  DataReductionProxyTypeInfo();
-  DataReductionProxyTypeInfo(const DataReductionProxyTypeInfo& other);
-  ~DataReductionProxyTypeInfo();
-  std::vector<net::ProxyServer> proxy_servers;
-  size_t proxy_index;
-};
-
 // Provides initialization parameters. Proxy origins, and the secure proxy
 // check url are are taken from flags if available and from preprocessor
 // constants otherwise. The DataReductionProxySettings class and others use this
@@ -178,6 +169,17 @@
   const std::vector<DataReductionProxyServer>& proxies_for_http()
       const override;
 
+  // Finds the first proxy in |proxies_for_http()| that matches |proxy_server|
+  // if any exist.
+  base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
+      const net::ProxyServer& proxy_server) const override;
+
+  // Helper function to locate |proxy_server| in |proxies| if it exists. This
+  // function is exposed publicly so that DataReductionProxyParams can use it.
+  static base::Optional<DataReductionProxyTypeInfo> FindConfiguredProxyInVector(
+      const std::vector<DataReductionProxyServer>& proxies,
+      const net::ProxyServer& proxy_server);
+
  private:
   std::vector<DataReductionProxyServer> proxies_for_http_;
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
index e79393a..b99579f4 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc
@@ -9,7 +9,7 @@
 namespace data_reduction_proxy {
 
 TestDataReductionProxyParams::TestDataReductionProxyParams()
-    : DataReductionProxyParams(), override_non_secure_proxies_(false) {
+    : override_non_secure_proxies_(false) {
   proxies_for_http_.push_back(DataReductionProxyServer(
       net::ProxyServer::FromURI("origin.net:80", net::ProxyServer::SCHEME_HTTP),
       ProxyServer::CORE));
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
index a6e181b..e49239c8 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
@@ -13,11 +13,13 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
+#include "base/optional.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "net/base/proxy_server.h"
@@ -30,27 +32,8 @@
 #endif
 
 namespace data_reduction_proxy {
-class DataReductionProxyParamsTest : public testing::Test {
- public:
-  void CheckValues(const TestDataReductionProxyParams& params,
-                   const std::string& expected_origin,
-                   const std::string& expected_fallback_origin) {
-    std::vector<net::ProxyServer> expected_proxies;
-    if (!expected_origin.empty()) {
-      expected_proxies.push_back(net::ProxyServer::FromURI(
-          expected_origin, net::ProxyServer::SCHEME_HTTP));
-    }
 
-    if (!expected_fallback_origin.empty()) {
-      expected_proxies.push_back(net::ProxyServer::FromURI(
-          expected_fallback_origin, net::ProxyServer::SCHEME_HTTP));
-    }
-
-    EXPECT_EQ(expected_proxies,
-              DataReductionProxyServer::ConvertToNetProxyServers(
-                  params.proxies_for_http()));
-  }
-};
+class DataReductionProxyParamsTest : public testing::Test {};
 
 TEST_F(DataReductionProxyParamsTest, EverythingDefined) {
   TestDataReductionProxyParams params;
@@ -67,6 +50,24 @@
       ProxyServer::CORE));
 
   EXPECT_EQ(expected_proxies, params.proxies_for_http());
+
+  EXPECT_FALSE(
+      params.FindConfiguredDataReductionProxy(net::ProxyServer::FromURI(
+          "unrelated.proxy.net:80", net::ProxyServer::SCHEME_HTTP)));
+
+  base::Optional<DataReductionProxyTypeInfo> first_info =
+      params.FindConfiguredDataReductionProxy(
+          expected_proxies[0].proxy_server());
+  ASSERT_TRUE(first_info);
+  EXPECT_EQ(expected_proxies, first_info->proxy_servers);
+  EXPECT_EQ(0U, first_info->proxy_index);
+
+  base::Optional<DataReductionProxyTypeInfo> second_info =
+      params.FindConfiguredDataReductionProxy(
+          expected_proxies[1].proxy_server());
+  ASSERT_TRUE(second_info);
+  EXPECT_EQ(expected_proxies, second_info->proxy_servers);
+  EXPECT_EQ(1U, second_info->proxy_index);
 }
 
 TEST_F(DataReductionProxyParamsTest, Flags) {
@@ -75,7 +76,40 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kDataReductionProxyFallback, "http://ovveride-2.com/");
   TestDataReductionProxyParams params;
-  CheckValues(params, "http://ovveride-1.com/", "http://ovveride-2.com/");
+
+  std::vector<DataReductionProxyServer> expected_proxies;
+  expected_proxies.push_back(DataReductionProxyServer(
+      net::ProxyServer::FromURI("http://ovveride-1.com/",
+                                net::ProxyServer::SCHEME_HTTP),
+      ProxyServer::UNSPECIFIED_TYPE));
+  expected_proxies.push_back(DataReductionProxyServer(
+      net::ProxyServer::FromURI("http://ovveride-2.com/",
+                                net::ProxyServer::SCHEME_HTTP),
+      ProxyServer::UNSPECIFIED_TYPE));
+
+  EXPECT_EQ(expected_proxies, params.proxies_for_http());
+
+  // The default proxies shouldn't be recognized as Data Reduction Proxies.
+  EXPECT_FALSE(
+      params.FindConfiguredDataReductionProxy(net::ProxyServer::FromURI(
+          "https://proxy.googlezip.net:443", net::ProxyServer::SCHEME_HTTP)));
+  EXPECT_FALSE(
+      params.FindConfiguredDataReductionProxy(net::ProxyServer::FromURI(
+          "compress.googlezip.net:80", net::ProxyServer::SCHEME_HTTP)));
+
+  base::Optional<DataReductionProxyTypeInfo> first_info =
+      params.FindConfiguredDataReductionProxy(
+          expected_proxies[0].proxy_server());
+  ASSERT_TRUE(first_info);
+  EXPECT_EQ(expected_proxies, first_info->proxy_servers);
+  EXPECT_EQ(0U, first_info->proxy_index);
+
+  base::Optional<DataReductionProxyTypeInfo> second_info =
+      params.FindConfiguredDataReductionProxy(
+          expected_proxies[1].proxy_server());
+  ASSERT_TRUE(second_info);
+  EXPECT_EQ(expected_proxies, second_info->proxy_servers);
+  EXPECT_EQ(1U, second_info->proxy_index);
 }
 
 TEST_F(DataReductionProxyParamsTest, IsClientConfigEnabled) {
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h
new file mode 100644
index 0000000..1137ee0
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h
@@ -0,0 +1,36 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_TYPE_INFO_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_TYPE_INFO_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+namespace data_reduction_proxy {
+
+class DataReductionProxyServer;
+
+// Contains information about a given proxy server.
+struct DataReductionProxyTypeInfo {
+  DataReductionProxyTypeInfo(
+      const std::vector<DataReductionProxyServer>& proxy_servers,
+      size_t proxy_index)
+      : proxy_servers(proxy_servers), proxy_index(proxy_index) {}
+
+  // The full configured list of proxy servers that includes the target proxy
+  // server. Since this is held onto as a const reference, the caller needs to
+  // ensure that it doesn't try to access |proxy_servers| if the list of
+  // configured proxies has changed since this DataReductionProxyTypeInfo was
+  // created.
+  const std::vector<DataReductionProxyServer>& proxy_servers;
+
+  // The index of this proxy in |proxy_servers|.
+  size_t proxy_index;
+};
+
+}  // namespace data_reduction_proxy
+
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_TYPE_INFO_H_
diff --git a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
index c4e98cee..2cd5e0e5 100644
--- a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
@@ -167,7 +167,7 @@
     // Search free lists for suitable span.
     std::unique_ptr<DiscardableSharedMemoryHeap::Span> free_span =
         heap_->SearchFreeLists(pages, slack);
-    if (!free_span.get())
+    if (!free_span)
       break;
 
     // Attempt to lock |free_span|. Delete span and search free lists again
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source_unittest.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source_unittest.cc
index df1fa73..5238069e 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source_unittest.cc
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source_unittest.cc
@@ -22,9 +22,8 @@
 };
 
 TEST_F(DomDistillerViewerSourceTest, TestMimeType) {
-  EXPECT_EQ("text/css", source_.get()->GetMimeType(kViewerCssPath));
-  EXPECT_EQ("text/html", source_.get()->GetMimeType("anythingelse"));
-
+  EXPECT_EQ("text/css", source_->GetMimeType(kViewerCssPath));
+  EXPECT_EQ("text/html", source_->GetMimeType("anythingelse"));
 }
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/distiller.cc b/components/dom_distiller/core/distiller.cc
index f3ebbd8f6..e741731 100644
--- a/components/dom_distiller/core/distiller.cc
+++ b/components/dom_distiller/core/distiller.cc
@@ -159,7 +159,7 @@
     }
   }
 
-  DCHECK(distiller_result.get());
+  DCHECK(distiller_result);
   DistilledPageData* page_data =
       GetPageAtIndex(started_pages_index_[page_num]);
   page_data->distilled_page_proto =
@@ -311,7 +311,7 @@
                                      const std::string& response) {
   DCHECK(started_pages_index_.find(page_num) != started_pages_index_.end());
   DistilledPageData* page_data = GetPageAtIndex(started_pages_index_[page_num]);
-  DCHECK(page_data->distilled_page_proto.get());
+  DCHECK(page_data->distilled_page_proto);
   DCHECK(url_fetcher);
   auto fetcher_it = std::find_if(
       page_data->image_fetchers_.begin(), page_data->image_fetchers_.end(),
diff --git a/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc b/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
index 102c2d5..f7d8dd9a1 100644
--- a/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
@@ -194,7 +194,7 @@
     std::unique_ptr<ArticleDistillationUpdate> article_update(
         new ArticleDistillationUpdate(pages, false, false));
 
-    handle.OnArticleUpdated(*article_update.get());
+    handle.OnArticleUpdated(*article_update);
 
     EXPECT_THAT(handle.GetJavaScriptBuffer(), HasSubstr(no_content));
     EXPECT_THAT(handle.GetJavaScriptBuffer(), Not(HasSubstr(valid_content)));
@@ -220,7 +220,7 @@
   std::unique_ptr<ArticleDistillationUpdate> article_update(
       new ArticleDistillationUpdate(pages, true, false));
 
-  handle.OnArticleUpdated(*article_update.get());
+  handle.OnArticleUpdated(*article_update);
 
   EXPECT_THAT(handle.GetJavaScriptBuffer(), HasSubstr(show_loader));
 }
diff --git a/components/dom_distiller/core/viewer_unittest.cc b/components/dom_distiller/core/viewer_unittest.cc
index d064704d..e7bfd5a 100644
--- a/components/dom_distiller/core/viewer_unittest.cc
+++ b/components/dom_distiller/core/viewer_unittest.cc
@@ -92,9 +92,9 @@
   std::unique_ptr<FakeViewRequestDelegate> view_request_delegate(
       new FakeViewRequestDelegate());
   ViewerHandle* viewer_handle(new ViewerHandle(ViewerHandle::CancelCallback()));
-  EXPECT_CALL(*service_.get(), ViewUrlImpl())
+  EXPECT_CALL(*service_, ViewUrlImpl())
       .WillOnce(testing::Return(viewer_handle));
-  EXPECT_CALL(*service_.get(), ViewEntryImpl()).Times(0);
+  EXPECT_CALL(*service_, ViewEntryImpl()).Times(0);
   CreateViewRequest(
       std::string("?") + kUrlKey + "=http%3A%2F%2Fwww.example.com%2F",
       view_request_delegate.get());
@@ -104,9 +104,9 @@
   std::unique_ptr<FakeViewRequestDelegate> view_request_delegate(
       new FakeViewRequestDelegate());
   ViewerHandle* viewer_handle(new ViewerHandle(ViewerHandle::CancelCallback()));
-  EXPECT_CALL(*service_.get(), ViewEntryImpl())
+  EXPECT_CALL(*service_, ViewEntryImpl())
       .WillOnce(testing::Return(viewer_handle));
-  EXPECT_CALL(*service_.get(), ViewUrlImpl()).Times(0);
+  EXPECT_CALL(*service_, ViewUrlImpl()).Times(0);
   CreateViewRequest(std::string("?") + kEntryIdKey + "=abc-def",
                     view_request_delegate.get());
 }
@@ -114,8 +114,8 @@
 TEST_F(DomDistillerViewerTest, TestCreatingInvalidViewRequest) {
   std::unique_ptr<FakeViewRequestDelegate> view_request_delegate(
       new FakeViewRequestDelegate());
-  EXPECT_CALL(*service_.get(), ViewEntryImpl()).Times(0);
-  EXPECT_CALL(*service_.get(), ViewUrlImpl()).Times(0);
+  EXPECT_CALL(*service_, ViewEntryImpl()).Times(0);
+  EXPECT_CALL(*service_, ViewUrlImpl()).Times(0);
   // Specify none of the required query parameters.
   CreateViewRequest("?foo=bar", view_request_delegate.get());
   // Specify both of the required query parameters.
diff --git a/components/dom_distiller_strings_grdp/OWNERS b/components/dom_distiller_strings_grdp/OWNERS
new file mode 100644
index 0000000..9db3893
--- /dev/null
+++ b/components/dom_distiller_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/dom_distiller/OWNERS
diff --git a/components/dom_distiller_strings_grdp/README.md b/components/dom_distiller_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/dom_distiller_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/domain_reliability/header.cc b/components/domain_reliability/header.cc
index 0d2136d..cefcd60 100644
--- a/components/domain_reliability/header.cc
+++ b/components/domain_reliability/header.cc
@@ -278,7 +278,7 @@
 }
 
 std::string DomainReliabilityHeader::ToString() const {
-  std::string string = "";
+  std::string string;
   int64_t max_age_s = max_age_.InSeconds();
 
   if (config_->collectors.empty()) {
@@ -314,7 +314,7 @@
     base::TimeDelta max_age)
     : status_(status), config_(std::move(config)), max_age_(max_age) {
   DCHECK_EQ(PARSE_SET_CONFIG, status_);
-  DCHECK(config_.get());
+  DCHECK(config_);
   DCHECK_NE(0, max_age_.InMicroseconds());
 }
 
diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc
index a5cf89e8..64cf9b1 100644
--- a/components/domain_reliability/monitor.cc
+++ b/components/domain_reliability/monitor.cc
@@ -336,7 +336,7 @@
     return;
 
   int response_code;
-  if (request.response_info.headers.get())
+  if (request.response_info.headers)
     response_code = request.response_info.headers->response_code();
   else
     response_code = -1;
@@ -394,7 +394,7 @@
 
 void DomainReliabilityMonitor::MaybeHandleHeader(
     const RequestInfo& request) {
-  if (!request.response_info.headers.get())
+  if (!request.response_info.headers)
     return;
 
   size_t iter = 0;
diff --git a/components/domain_reliability/service.cc b/components/domain_reliability/service.cc
index ed369b8..3efe542e 100644
--- a/components/domain_reliability/service.cc
+++ b/components/domain_reliability/service.cc
@@ -65,7 +65,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
       override {
-    DCHECK(!network_task_runner_.get());
+    DCHECK(!network_task_runner_);
 
     std::unique_ptr<DomainReliabilityMonitor> monitor(
         new DomainReliabilityMonitor(
@@ -86,7 +86,7 @@
       DomainReliabilityClearMode clear_mode,
       const base::Callback<bool(const GURL&)>& origin_filter,
       const base::Closure& callback) override {
-    DCHECK(network_task_runner_.get());
+    DCHECK(network_task_runner_);
 
     network_task_runner_->PostTaskAndReply(
         FROM_HERE, base::Bind(&DomainReliabilityMonitor::ClearBrowsingData,
@@ -96,7 +96,7 @@
 
   void GetWebUIData(const base::Callback<void(std::unique_ptr<base::Value>)>&
                         callback) const override {
-    DCHECK(network_task_runner_.get());
+    DCHECK(network_task_runner_);
 
     PostTaskAndReplyWithResult(
         network_task_runner_.get(),
@@ -106,7 +106,7 @@
   }
 
   void SetDiscardUploadsForTesting(bool discard_uploads) override {
-    DCHECK(network_task_runner_.get());
+    DCHECK(network_task_runner_);
 
     network_task_runner_->PostTask(
         FROM_HERE,
@@ -117,7 +117,7 @@
 
   void AddContextForTesting(
       std::unique_ptr<const DomainReliabilityConfig> config) override {
-    DCHECK(network_task_runner_.get());
+    DCHECK(network_task_runner_);
 
     network_task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&AddContextForTestingOnNetworkTaskRunner,
@@ -125,7 +125,7 @@
   }
 
   void ForceUploadsForTesting() override {
-    DCHECK(network_task_runner_.get());
+    DCHECK(network_task_runner_);
 
     network_task_runner_->PostTask(
         FROM_HERE,
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index 6b2f626..f0d9a07 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -84,7 +84,7 @@
           : item->GetFullPath();
   entry.completion_time = item->GetEndTime();
   entry.response_headers = item->GetResponseHeaders();
-  if (entry.response_headers.get()) {
+  if (entry.response_headers) {
     entry.can_resume =
         entry.response_headers->HasHeaderValue("Accept-Ranges", "bytes") ||
         (entry.response_headers->HasHeader("Content-Range") &&
diff --git a/components/download/content/public/all_download_item_notifier_unittest.cc b/components/download/content/public/all_download_item_notifier_unittest.cc
index 5fc8e582..b893c8a 100644
--- a/components/download/content/public/all_download_item_notifier_unittest.cc
+++ b/components/download/content/public/all_download_item_notifier_unittest.cc
@@ -42,7 +42,7 @@
 
   ~AllDownloadItemNotifierTest() override {}
 
-  content::MockDownloadManager& manager() { return *download_manager_.get(); }
+  content::MockDownloadManager& manager() { return *download_manager_; }
 
   download::MockDownloadItem& item() { return item_; }
 
@@ -57,7 +57,7 @@
   MockNotifierObserver& observer() { return observer_; }
 
   void SetNotifier() {
-    EXPECT_CALL(*download_manager_.get(), AddObserver(_));
+    EXPECT_CALL(*download_manager_, AddObserver(_));
     notifier_.reset(
         new AllDownloadItemNotifier(download_manager_.get(), &observer_));
   }
diff --git a/components/download/internal/background_service/blob_task_proxy.cc b/components/download/internal/background_service/blob_task_proxy.cc
index ab52593..4c08139 100644
--- a/components/download/internal/background_service/blob_task_proxy.cc
+++ b/components/download/internal/background_service/blob_task_proxy.cc
@@ -61,7 +61,7 @@
   // Build blob data. This has to do a copy into blob's internal storage.
   std::string blob_uuid = base::GenerateGUID();
   auto builder = std::make_unique<storage::BlobDataBuilder>(blob_uuid);
-  builder->AppendData(*data.get());
+  builder->AppendData(*data);
   blob_data_handle_ =
       blob_storage_context_->AddFinishedBlob(std::move(builder));
 
diff --git a/components/download/internal/background_service/in_memory_download_driver.cc b/components/download/internal/background_service/in_memory_download_driver.cc
index 99ba097..50eeda04 100644
--- a/components/download/internal/background_service/in_memory_download_driver.cc
+++ b/components/download/internal/background_service/in_memory_download_driver.cc
@@ -131,7 +131,7 @@
   base::Optional<DriverEntry> entry;
   auto it = downloads_.find(guid);
   if (it != downloads_.end())
-    entry = CreateDriverEntry(*it->second.get());
+    entry = CreateDriverEntry(*it->second);
   return entry;
 }
 
diff --git a/components/download/internal/background_service/scheduler/device_status_listener_unittest.cc b/components/download/internal/background_service/scheduler/device_status_listener_unittest.cc
index 18ab129..74ceabe5 100644
--- a/components/download/internal/background_service/scheduler/device_status_listener_unittest.cc
+++ b/components/download/internal/background_service/scheduler/device_status_listener_unittest.cc
@@ -34,8 +34,7 @@
 class TestNetworkChangeNotifier : public net::NetworkChangeNotifier {
  public:
   TestNetworkChangeNotifier()
-      : net::NetworkChangeNotifier(),
-        conn_type_(ConnectionType::CONNECTION_UNKNOWN) {}
+      : conn_type_(ConnectionType::CONNECTION_UNKNOWN) {}
 
   // net::NetworkChangeNotifier implementation.
   ConnectionType GetCurrentConnectionType() const override {
@@ -129,7 +128,7 @@
   // Simulates a network change call, the event will be sent to client
   // immediately.
   void ChangeNetworkTypeImmediately(ConnectionType type) {
-    DCHECK(listener_.get());
+    DCHECK(listener_);
     static_cast<NetworkStatusListener::Observer*>(listener_.get())
         ->OnNetworkChanged(type);
   }
diff --git a/components/download/internal/common/base_file_win.cc b/components/download/internal/common/base_file_win.cc
index df78b695..f7facc3 100644
--- a/components/download/internal/common/base_file_win.cc
+++ b/components/download/internal/common/base_file_win.cc
@@ -302,7 +302,7 @@
   source.append(1, L'\0');
   target.append(1, L'\0');
 
-  SHFILEOPSTRUCT move_info = {0};
+  SHFILEOPSTRUCT move_info = {nullptr};
   move_info.wFunc = FO_MOVE;
   move_info.pFrom = source.c_str();
   move_info.pTo = target.c_str();
diff --git a/components/download/internal/common/download_file_impl.cc b/components/download/internal/common/download_file_impl.cc
index a1b4b684..eefdd2ce 100644
--- a/components/download/internal/common/download_file_impl.cc
+++ b/components/download/internal/common/download_file_impl.cc
@@ -504,7 +504,7 @@
         DCHECK_GE(incoming_data_size, bytes_to_write);
         reason = WriteDataToFile(
             source_stream->offset() + source_stream->bytes_written(),
-            incoming_data.get()->data(), bytes_to_write);
+            incoming_data->data(), bytes_to_write);
         disk_writes_time_ += (base::TimeTicks::Now() - write_start);
         bytes_seen_ += bytes_to_write;
         total_incoming_data_size += bytes_to_write;
diff --git a/components/download/internal/common/download_file_unittest.cc b/components/download/internal/common/download_file_unittest.cc
index c0bfc9fb..3fb6077 100644
--- a/components/download/internal/common/download_file_unittest.cc
+++ b/components/download/internal/common/download_file_unittest.cc
@@ -201,7 +201,7 @@
                           bool calculate_hash,
                           const DownloadItem::ReceivedSlices& received_slices) {
     // There can be only one.
-    DCHECK(!download_file_.get());
+    DCHECK(!download_file_);
 
     input_stream_ = new StrictMock<MockInputStream>();
 
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index 638e372..1574628 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -418,7 +418,7 @@
 
   // Should always have been nuked before now, at worst in
   // DownloadManager shutdown.
-  DCHECK(!download_file_.get());
+  DCHECK(!download_file_);
   CHECK(!is_updating_observers_);
 
   for (auto& observer : observers_)
@@ -1045,7 +1045,7 @@
         IsPaused() ? 'T' : 'F', DebugResumeModeString(GetResumeMode()),
         auto_resume_count_, GetDangerType(), AllDataSaved() ? 'T' : 'F',
         GetLastModifiedTime().c_str(), GetETag().c_str(),
-        download_file_.get() ? "true" : "false", url_list.c_str(),
+        download_file_ ? "true" : "false", url_list.c_str(),
         GetFullPath().value().c_str(), GetTargetFilePath().value().c_str(),
         GetReferrerUrl().spec().c_str(), GetSiteUrl().spec().c_str());
   } else {
@@ -1392,7 +1392,7 @@
         url_loader_factory_getter,
     net::URLRequestContextGetter* url_request_context_getter) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!download_file_.get());
+  DCHECK(!download_file_);
   DVLOG(20) << __func__ << "() this=" << DebugString(true);
   RecordDownloadCountWithSource(START_COUNT, download_source_);
 
@@ -1428,7 +1428,7 @@
   // If a resumption attempted failed, or if the download was DOA, then the
   // download should go back to being interrupted.
   if (new_create_info.result != DOWNLOAD_INTERRUPT_REASON_NONE) {
-    DCHECK(!download_file_.get());
+    DCHECK(!download_file_);
 
     // Download requests that are interrupted by Start() should result in a
     // DownloadCreateInfo with an intact DownloadSaveInfo.
@@ -1721,7 +1721,7 @@
   DCHECK(!GetTargetFilePath().empty());
   DCHECK(!IsDangerous());
 
-  DCHECK(download_file_.get());
+  DCHECK(download_file_);
   // Unilaterally rename; even if it already has the right name,
   // we need theannotation.
   DownloadFile::RenameCompletionCallback callback =
diff --git a/components/download/internal/common/download_item_impl_unittest.cc b/components/download/internal/common/download_item_impl_unittest.cc
index 73de27f..1503ec3 100644
--- a/components/download/internal/common/download_item_impl_unittest.cc
+++ b/components/download/internal/common/download_item_impl_unittest.cc
@@ -66,7 +66,7 @@
 
 class MockDelegate : public DownloadItemImplDelegate {
  public:
-  MockDelegate() : DownloadItemImplDelegate() { SetDefaultExpectations(); }
+  MockDelegate() { SetDefaultExpectations(); }
 
   MOCK_METHOD2(DetermineDownloadTarget,
                void(DownloadItemImpl*,
@@ -2235,8 +2235,7 @@
       public ::testing::WithParamInterface<EventList> {
  public:
   DownloadItemDestinationUpdateRaceTest()
-      : DownloadItemTest(),
-        item_(CreateDownloadItem()),
+      : item_(CreateDownloadItem()),
         file_(new ::testing::StrictMock<MockDownloadFile>()),
         request_handle_(new ::testing::StrictMock<MockRequestHandle>()) {
     DCHECK_EQ(GetParam().size(), static_cast<unsigned>(kEventCount));
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 7ad88f9..9ed647a 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -373,7 +373,7 @@
 
   std::unique_ptr<DownloadFile> download_file;
   if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) {
-    DCHECK(stream.get());
+    DCHECK(stream);
     download_file.reset(file_factory_->CreateFile(
         std::move(info->save_info), default_download_directory,
         std::move(stream), id, download->DestinationObserverAsWeakPtr()));
diff --git a/components/download/internal/common/parallel_download_job_unittest.cc b/components/download/internal/common/parallel_download_job_unittest.cc
index eb09f931..ff5e9bc 100644
--- a/components/download/internal/common/parallel_download_job_unittest.cc
+++ b/components/download/internal/common/parallel_download_job_unittest.cc
@@ -429,7 +429,7 @@
   for (auto& worker : job_->workers()) {
     std::unique_ptr<MockDownloadRequestHandle> mock_handle =
         std::make_unique<MockDownloadRequestHandle>();
-    EXPECT_CALL(*mock_handle.get(), CancelRequest(_));
+    EXPECT_CALL(*mock_handle, CancelRequest(_));
     MakeWorkerReady(worker.second.get(), std::move(mock_handle));
   }
 
@@ -451,10 +451,10 @@
   EXPECT_TRUE(job_->is_paused());
 
   for (auto& worker : job_->workers()) {
-    EXPECT_CALL(*job_.get(), CountOnInputStreamReady());
+    EXPECT_CALL(*job_, CountOnInputStreamReady());
     std::unique_ptr<MockDownloadRequestHandle> mock_handle =
         std::make_unique<MockDownloadRequestHandle>();
-    EXPECT_CALL(*mock_handle.get(), PauseRequest());
+    EXPECT_CALL(*mock_handle, PauseRequest());
     MakeWorkerReady(worker.second.get(), std::move(mock_handle));
   }
 
@@ -517,7 +517,7 @@
   job_->MakeFileInitialized(callback.Get(), DOWNLOAD_INTERRUPT_REASON_NONE);
 
   // Simulate and inject an error from IO thread after file initialized.
-  EXPECT_CALL(*download_item_.get(), GetState())
+  EXPECT_CALL(*download_item_, GetState())
       .WillRepeatedly(Return(DownloadItem::DownloadState::INTERRUPTED));
 
   // Because of the error, no parallel requests are built.
diff --git a/components/download/quarantine/quarantine_win.cc b/components/download/quarantine/quarantine_win.cc
index 165d5e8..f240414 100644
--- a/components/download/quarantine/quarantine_win.cc
+++ b/components/download/quarantine/quarantine_win.cc
@@ -74,7 +74,8 @@
   std::vector<char> zone_identifier_contents_buffer(32);
   DWORD actual_length = 0;
   if (!ReadFile(file.Get(), &zone_identifier_contents_buffer.front(),
-                zone_identifier_contents_buffer.size(), &actual_length, NULL))
+                zone_identifier_contents_buffer.size(), &actual_length,
+                nullptr))
     return false;
   zone_identifier_contents_buffer.resize(actual_length);
 
diff --git a/components/drive/BUILD.gn b/components/drive/BUILD.gn
index 067d5ba..29b6e0c 100644
--- a/components/drive/BUILD.gn
+++ b/components/drive/BUILD.gn
@@ -140,6 +140,8 @@
       "chromeos/file_system_interface.cc",
       "chromeos/file_system_interface.h",
       "chromeos/file_system_observer.h",
+      "chromeos/loader_controller.cc",
+      "chromeos/loader_controller.h",
       "chromeos/remove_stale_cache_files.cc",
       "chromeos/remove_stale_cache_files.h",
       "chromeos/resource_metadata.cc",
diff --git a/components/drive/change_list_loader_unittest.cc b/components/drive/change_list_loader_unittest.cc
index d508c48..4dbbb0c 100644
--- a/components/drive/change_list_loader_unittest.cc
+++ b/components/drive/change_list_loader_unittest.cc
@@ -19,6 +19,7 @@
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/drive_test_util.h"
 #include "components/drive/chromeos/file_cache.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/event_logger.h"
 #include "components/drive/file_change.h"
diff --git a/components/drive/chromeos/change_list_loader.cc b/components/drive/chromeos/change_list_loader.cc
index 1c0f787..f75fb08 100644
--- a/components/drive/chromeos/change_list_loader.cc
+++ b/components/drive/chromeos/change_list_loader.cc
@@ -20,6 +20,7 @@
 #include "components/drive/chromeos/about_resource_loader.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/change_list_processor.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive_api_util.h"
 #include "components/drive/event_logger.h"
@@ -225,47 +226,6 @@
 
 }  // namespace
 
-LoaderController::LoaderController()
-    : lock_count_(0),
-      weak_ptr_factory_(this) {
-}
-
-LoaderController::~LoaderController() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-std::unique_ptr<base::ScopedClosureRunner> LoaderController::GetLock() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  ++lock_count_;
-  return std::make_unique<base::ScopedClosureRunner>(
-      base::Bind(&LoaderController::Unlock, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void LoaderController::ScheduleRun(const base::Closure& task) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!task.is_null());
-
-  if (lock_count_ > 0) {
-    pending_tasks_.push_back(task);
-  } else {
-    task.Run();
-  }
-}
-
-void LoaderController::Unlock() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_LT(0, lock_count_);
-
-  if (--lock_count_ > 0)
-    return;
-
-  std::vector<base::Closure> tasks;
-  tasks.swap(pending_tasks_);
-  for (size_t i = 0; i < tasks.size(); ++i)
-    tasks[i].Run();
-}
-
 
 ChangeListLoader::ChangeListLoader(
     EventLogger* logger,
diff --git a/components/drive/chromeos/change_list_loader.h b/components/drive/chromeos/change_list_loader.h
index 7bb89eb..511ef59 100644
--- a/components/drive/chromeos/change_list_loader.h
+++ b/components/drive/chromeos/change_list_loader.h
@@ -22,7 +22,6 @@
 
 namespace base {
 class CancellationFlag;
-class ScopedClosureRunner;
 class SequencedTaskRunner;
 class Time;
 }  // namespace base
@@ -42,39 +41,9 @@
 class ChangeList;
 class ChangeListLoaderObserver;
 class ChangeListProcessor;
+class LoaderController;
 class ResourceMetadata;
 
-// Delays execution of tasks as long as more than one lock is alive.
-// Used to ensure that ChangeListLoader does not cause race condition by adding
-// new entries created by sync tasks before they do.
-// All code which may add entries found on the server to the local metadata
-// should use this class.
-class LoaderController {
- public:
-  LoaderController();
-  ~LoaderController();
-
-  // Increments the lock count and returns an object which decrements the count
-  // on its destruction.
-  // While the lock count is positive, tasks will be pending.
-  std::unique_ptr<base::ScopedClosureRunner> GetLock();
-
-  // Runs the task if the lock count is 0, otherwise it will be pending.
-  void ScheduleRun(const base::Closure& task);
-
- private:
-  // Decrements the lock count.
-  void Unlock();
-
-  int lock_count_;
-  std::vector<base::Closure> pending_tasks_;
-
-  base::ThreadChecker thread_checker_;
-
-  base::WeakPtrFactory<LoaderController> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(LoaderController);
-};
-
 // ChangeListLoader is used to load the change list, the full resource list,
 // and directory contents, from Google Drive API.  The class also updates the
 // resource metadata with the change list loaded from the server.
diff --git a/components/drive/chromeos/directory_loader.cc b/components/drive/chromeos/directory_loader.cc
index 995caf0..fa1e1937 100644
--- a/components/drive/chromeos/directory_loader.cc
+++ b/components/drive/chromeos/directory_loader.cc
@@ -17,9 +17,9 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/drive/chromeos/about_resource_loader.h"
-#include "components/drive/chromeos/change_list_loader.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/change_list_processor.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive_api_util.h"
 #include "components/drive/event_logger.h"
@@ -108,8 +108,7 @@
         weak_ptr_factory_(this) {
   }
 
-  ~FeedFetcher() {
-  }
+  ~FeedFetcher() = default;
 
   void Run(const FileOperationCallback& callback) {
     DCHECK(thread_checker_.CalledOnValidThread());
@@ -212,8 +211,7 @@
       weak_ptr_factory_(this) {
 }
 
-DirectoryLoader::~DirectoryLoader() {
-}
+DirectoryLoader::~DirectoryLoader() = default;
 
 void DirectoryLoader::AddObserver(ChangeListLoaderObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/drive/chromeos/file_system.cc b/components/drive/chromeos/file_system.cc
index 48cafdfe..1555ac5b 100644
--- a/components/drive/chromeos/file_system.cc
+++ b/components/drive/chromeos/file_system.cc
@@ -28,6 +28,7 @@
 #include "components/drive/chromeos/file_system/touch_operation.h"
 #include "components/drive/chromeos/file_system/truncate_operation.h"
 #include "components/drive/chromeos/file_system_observer.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/remove_stale_cache_files.h"
 #include "components/drive/chromeos/search_metadata.h"
 #include "components/drive/chromeos/sync_client.h"
diff --git a/components/drive/chromeos/file_system/search_operation.cc b/components/drive/chromeos/file_system/search_operation.cc
index 649578e..e527e07 100644
--- a/components/drive/chromeos/file_system/search_operation.cc
+++ b/components/drive/chromeos/file_system/search_operation.cc
@@ -14,7 +14,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "components/drive/chromeos/change_list_loader.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive_api_util.h"
 #include "components/drive/file_system_core_util.h"
diff --git a/components/drive/chromeos/loader_controller.cc b/components/drive/chromeos/loader_controller.cc
new file mode 100644
index 0000000..cfd7dd416
--- /dev/null
+++ b/components/drive/chromeos/loader_controller.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/drive/chromeos/loader_controller.h"
+
+#include "base/callback_helpers.h"
+
+namespace drive {
+namespace internal {
+
+LoaderController::LoaderController()
+    : lock_count_(0), weak_ptr_factory_(this) {}
+
+LoaderController::~LoaderController() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+std::unique_ptr<base::ScopedClosureRunner> LoaderController::GetLock() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  ++lock_count_;
+  return std::make_unique<base::ScopedClosureRunner>(base::BindOnce(
+      &LoaderController::Unlock, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LoaderController::ScheduleRun(const base::RepeatingClosure& task) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(task);
+
+  if (lock_count_ > 0) {
+    pending_tasks_.push_back(task);
+  } else {
+    task.Run();
+  }
+}
+
+void LoaderController::Unlock() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_LT(0, lock_count_);
+
+  if (--lock_count_ > 0)
+    return;
+
+  std::vector<base::RepeatingClosure> tasks;
+  tasks.swap(pending_tasks_);
+  for (auto& task : tasks)
+    task.Run();
+}
+
+}  // namespace internal
+}  // namespace drive
diff --git a/components/drive/chromeos/loader_controller.h b/components/drive/chromeos/loader_controller.h
new file mode 100644
index 0000000..26ca87b3
--- /dev/null
+++ b/components/drive/chromeos/loader_controller.h
@@ -0,0 +1,62 @@
+// 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 COMPONENTS_DRIVE_CHROMEOS_LOADER_CONTROLLER_H_
+#define COMPONENTS_DRIVE_CHROMEOS_LOADER_CONTROLLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+class ScopedClosureRunner;
+}
+
+namespace drive {
+namespace internal {
+
+// Delays execution of tasks as long as more than one lock is alive.
+// Used to ensure that ChangeListLoader does not cause race condition by adding
+// new entries created by sync tasks before they do.
+// All code which may add entries found on the server to the local metadata
+// should use this class.
+// TODO(slangley): Consider specific unit tests for LoaderController. Currently
+// it is tested as an artifact of other unit tests.
+class LoaderController {
+ public:
+  LoaderController();
+  ~LoaderController();
+
+  // Increments the lock count and returns an object which decrements the count
+  // on its destruction.
+  // While the lock count is positive, tasks will be pending.
+  // TODO(slangley): Return ScopedClosureRunner directly, rather than one that
+  // is wrapped in a unique_ptr.
+  std::unique_ptr<base::ScopedClosureRunner> GetLock();
+
+  // Runs the task if the lock count is 0, otherwise it will be pending.
+  // TODO(slangley): Change to using a OnceClosure.
+  void ScheduleRun(const base::Closure& task);
+
+ private:
+  // Decrements the lock count.
+  void Unlock();
+
+  int lock_count_;
+  std::vector<base::RepeatingClosure> pending_tasks_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  base::WeakPtrFactory<LoaderController> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(LoaderController);
+};
+
+}  // namespace internal
+}  // namespace drive
+
+#endif  // COMPONENTS_DRIVE_CHROMEOS_LOADER_CONTROLLER_H_
diff --git a/components/drive/chromeos/sync/entry_update_performer.cc b/components/drive/chromeos/sync/entry_update_performer.cc
index b0855c3..1276e11 100644
--- a/components/drive/chromeos/sync/entry_update_performer.cc
+++ b/components/drive/chromeos/sync/entry_update_performer.cc
@@ -10,9 +10,9 @@
 
 #include "base/callback_helpers.h"
 #include "base/files/file_util.h"
-#include "components/drive/chromeos/change_list_loader.h"
 #include "components/drive/chromeos/file_cache.h"
 #include "components/drive/chromeos/file_system/operation_delegate.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/chromeos/sync/entry_revert_performer.h"
 #include "components/drive/chromeos/sync/remove_performer.h"
diff --git a/components/drive/directory_loader_unittest.cc b/components/drive/directory_loader_unittest.cc
index 304cfe17..c7d8676a 100644
--- a/components/drive/directory_loader_unittest.cc
+++ b/components/drive/directory_loader_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/drive/chromeos/about_resource_loader.h"
-#include "components/drive/chromeos/change_list_loader.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/drive_test_util.h"
 #include "components/drive/chromeos/file_cache.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/event_logger.h"
 #include "components/drive/file_system_core_util.h"
diff --git a/components/drive/drive_uploader.cc b/components/drive/drive_uploader.cc
index c9bf1721..bc7d5f2 100644
--- a/components/drive/drive_uploader.cc
+++ b/components/drive/drive_uploader.cc
@@ -309,7 +309,7 @@
   if (info_ptr->content_length <= kMaxMultipartUploadSize) {
     DriveServiceBatchOperationsInterface* service;
     // If this is a batched request, calls the API on the request instead.
-    if (batch_request.get()) {
+    if (batch_request) {
       service = batch_request->configurator();
       RecordDriveUploadProtocol(UPLOAD_METHOD_BATCH);
     } else {
@@ -344,7 +344,7 @@
   if (info_ptr->content_length <= kMaxMultipartUploadSize) {
     DriveServiceBatchOperationsInterface* service;
     // If this is a batched request, calls the API on the request instead.
-    if (batch_request.get()) {
+    if (batch_request) {
       service = batch_request->configurator();
       RecordDriveUploadProtocol(UPLOAD_METHOD_BATCH);
     } else {
diff --git a/components/drive/file_system/operation_test_base.cc b/components/drive/file_system/operation_test_base.cc
index 393241f..4fe6587 100644
--- a/components/drive/file_system/operation_test_base.cc
+++ b/components/drive/file_system/operation_test_base.cc
@@ -10,6 +10,7 @@
 #include "components/drive/chromeos/fake_free_disk_space_getter.h"
 #include "components/drive/chromeos/file_cache.h"
 #include "components/drive/chromeos/file_system/operation_delegate.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/event_logger.h"
 #include "components/drive/file_change.h"
@@ -23,11 +24,9 @@
 namespace drive {
 namespace file_system {
 
-OperationTestBase::LoggingDelegate::LoggingDelegate() {
-}
+OperationTestBase::LoggingDelegate::LoggingDelegate() = default;
 
-OperationTestBase::LoggingDelegate::~LoggingDelegate() {
-}
+OperationTestBase::LoggingDelegate::~LoggingDelegate() = default;
 
 void OperationTestBase::LoggingDelegate::OnFileChangedByOperation(
     const FileChange& changed_files) {
@@ -52,15 +51,13 @@
       false : wait_for_sync_complete_handler_.Run(local_id, callback);
 }
 
-OperationTestBase::OperationTestBase() {
-}
+OperationTestBase::OperationTestBase() = default;
 
 OperationTestBase::OperationTestBase(int test_thread_bundle_options)
     : thread_bundle_(test_thread_bundle_options) {
 }
 
-OperationTestBase::~OperationTestBase() {
-}
+OperationTestBase::~OperationTestBase() = default;
 
 void OperationTestBase::SetUp() {
   blocking_task_runner_ =
diff --git a/components/drive/file_system/search_operation_unittest.cc b/components/drive/file_system/search_operation_unittest.cc
index c8837191..28c1bb7 100644
--- a/components/drive/file_system/search_operation_unittest.cc
+++ b/components/drive/file_system/search_operation_unittest.cc
@@ -7,7 +7,7 @@
 #include <stddef.h>
 
 #include "base/callback_helpers.h"
-#include "components/drive/chromeos/change_list_loader.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/file_system/operation_test_base.h"
 #include "components/drive/service/fake_drive_service.h"
 #include "content/public/test/test_utils.h"
diff --git a/components/drive/service/drive_api_service.cc b/components/drive/service/drive_api_service.cc
index 31a56ea..5f17ad6 100644
--- a/components/drive/service/drive_api_service.cc
+++ b/components/drive/service/drive_api_service.cc
@@ -281,7 +281,7 @@
 
 DriveAPIService::~DriveAPIService() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (sender_.get())
+  if (sender_)
     sender_->auth_service()->RemoveObserver(this);
 }
 
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
index e35c04c..7a2bb4c8 100644
--- a/components/drive/service/fake_drive_service.cc
+++ b/components/drive/service/fake_drive_service.cc
@@ -1868,8 +1868,7 @@
         max_results));
     if (start_changestamp > 0) {
       next_url = net::AppendOrReplaceQueryParameter(
-          next_url, "changestamp",
-          base::Int64ToString(start_changestamp).c_str());
+          next_url, "changestamp", base::Int64ToString(start_changestamp));
     }
     if (!search_query.empty()) {
       next_url = net::AppendOrReplaceQueryParameter(
diff --git a/components/drive/service/fake_drive_service_unittest.cc b/components/drive/service/fake_drive_service_unittest.cc
index 4f28a3c..9b944ac7 100644
--- a/components/drive/service/fake_drive_service_unittest.cc
+++ b/components/drive/service/fake_drive_service_unittest.cc
@@ -1836,7 +1836,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(DRIVE_NO_CONNECTION, response.code);
-  EXPECT_FALSE(entry.get());
+  EXPECT_FALSE(entry);
 }
 
 TEST_F(FakeDriveServiceTest, ResumeUpload_NotFound) {
@@ -1863,7 +1863,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(HTTP_NOT_FOUND, response.code);
-  EXPECT_FALSE(entry.get());
+  EXPECT_FALSE(entry);
 }
 
 TEST_F(FakeDriveServiceTest, ResumeUpload_ExistingFile) {
@@ -1907,7 +1907,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
-  EXPECT_FALSE(entry.get());
+  EXPECT_FALSE(entry);
   ASSERT_TRUE(!upload_progress_values.empty());
   EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
   EXPECT_LE(0, upload_progress_values.front().first);
@@ -1972,7 +1972,7 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
-  EXPECT_FALSE(entry.get());
+  EXPECT_FALSE(entry);
   ASSERT_TRUE(!upload_progress_values.empty());
   EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
   EXPECT_LE(0, upload_progress_values.front().first);
diff --git a/components/drive/sync_client_unittest.cc b/components/drive/sync_client_unittest.cc
index 5e1dcbf..04f61d2 100644
--- a/components/drive/sync_client_unittest.cc
+++ b/components/drive/sync_client_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/drive/chromeos/file_system/move_operation.h"
 #include "components/drive/chromeos/file_system/operation_delegate.h"
 #include "components/drive/chromeos/file_system/remove_operation.h"
+#include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive.pb.h"
 #include "components/drive/event_logger.h"
diff --git a/components/error_page_strings_grdp/OWNERS b/components/error_page_strings_grdp/OWNERS
new file mode 100644
index 0000000..1a8ff93
--- /dev/null
+++ b/components/error_page_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/error_page/OWNERS
diff --git a/components/error_page_strings_grdp/README.md b/components/error_page_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/error_page_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/exo/notification_surface.cc b/components/exo/notification_surface.cc
index 17ae6b18..6c3209a 100644
--- a/components/exo/notification_surface.cc
+++ b/components/exo/notification_surface.cc
@@ -5,6 +5,7 @@
 #include "components/exo/notification_surface.h"
 
 #include "components/exo/notification_surface_manager.h"
+#include "components/exo/shell_surface.h"
 #include "components/exo/surface.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/geometry/rect.h"
@@ -33,6 +34,11 @@
   return root_surface()->content_size();
 }
 
+void NotificationSurface::SetApplicationId(const char* application_id) {
+  exo::ShellSurface::SetApplicationId(host_window(),
+                                      base::make_optional(application_id));
+}
+
 void NotificationSurface::OnSurfaceCommit() {
   SurfaceTreeHost::OnSurfaceCommit();
 
diff --git a/components/exo/notification_surface.h b/components/exo/notification_surface.h
index a202ba6..2544e40c 100644
--- a/components/exo/notification_surface.h
+++ b/components/exo/notification_surface.h
@@ -28,6 +28,8 @@
   // Get the content size of the |root_surface()|.
   const gfx::Size& GetContentSize() const;
 
+  void SetApplicationId(const char* application_id);
+
   const std::string& notification_key() const { return notification_key_; }
 
   // Overridden from SurfaceDelegate:
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index fd74cd44..267b4540 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -2347,8 +2347,15 @@
   wl_resource_destroy(resource);
 }
 
+void notification_surface_set_app_id(wl_client* client,
+                                     wl_resource* resource,
+                                     const char* app_id) {
+  GetUserDataAs<NotificationSurface>(resource)->SetApplicationId(app_id);
+}
+
 const struct zcr_notification_surface_v1_interface
-    notification_surface_implementation = {notification_surface_destroy};
+    notification_surface_implementation = {notification_surface_destroy,
+                                           notification_surface_set_app_id};
 
 ////////////////////////////////////////////////////////////////////////////////
 // remote_shell_interface:
@@ -3027,7 +3034,7 @@
 const struct zaura_shell_interface aura_shell_implementation = {
     aura_shell_get_aura_surface, aura_shell_get_aura_output};
 
-const uint32_t aura_shell_version = 5;
+const uint32_t aura_shell_version = 6;
 
 void bind_aura_shell(wl_client* client,
                      void* data,
diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn
index 59f1e72..798ad7bc 100644
--- a/components/favicon/core/BUILD.gn
+++ b/components/favicon/core/BUILD.gn
@@ -14,6 +14,8 @@
     "favicon_driver_observer.h",
     "favicon_handler.cc",
     "favicon_handler.h",
+    "favicon_server_fetcher_params.cc",
+    "favicon_server_fetcher_params.h",
     "favicon_service.cc",
     "favicon_service.h",
     "favicon_service_impl.cc",
diff --git a/components/favicon/core/favicon_server_fetcher_params.cc b/components/favicon/core/favicon_server_fetcher_params.cc
new file mode 100644
index 0000000..dd23dd9
--- /dev/null
+++ b/components/favicon/core/favicon_server_fetcher_params.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 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 "components/favicon/core/favicon_server_fetcher_params.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/favicon_base/favicon_util.h"
+#include "ui/gfx/favicon_size.h"
+
+namespace favicon {
+namespace {
+
+const char kClientParamDesktop[] = "client=chrome_desktop";
+const char kClientParamMobile[] = "client=chrome";
+
+float GetMaxDeviceScaleFactor() {
+  std::vector<float> favicon_scales = favicon_base::GetFaviconScales();
+  DCHECK(!favicon_scales.empty());
+  return favicon_scales.back();
+}
+
+}  // namespace
+
+std::unique_ptr<FaviconServerFetcherParams>
+FaviconServerFetcherParams::CreateForDesktop(const GURL& page_url) {
+  return base::WrapUnique(new FaviconServerFetcherParams(
+      page_url, favicon_base::IconType::kFavicon,
+      std::ceil(gfx::kFaviconSize * GetMaxDeviceScaleFactor()), 0,
+      kClientParamDesktop));
+}
+
+std::unique_ptr<FaviconServerFetcherParams>
+FaviconServerFetcherParams::CreateForMobile(const GURL& page_url,
+                                            int min_source_size_in_pixel,
+                                            int desired_size_in_pixel) {
+  return base::WrapUnique(new FaviconServerFetcherParams(
+      page_url, favicon_base::IconType::kTouchIcon, min_source_size_in_pixel,
+      desired_size_in_pixel, kClientParamMobile));
+}
+
+FaviconServerFetcherParams::FaviconServerFetcherParams(
+    const GURL& page_url,
+    favicon_base::IconType icon_type,
+    int min_source_size_in_pixel,
+    int desired_size_in_pixel,
+    const std::string& google_server_client_param)
+    : page_url_(page_url),
+      icon_type_(icon_type),
+      min_source_size_in_pixel_(min_source_size_in_pixel),
+      desired_size_in_pixel_(desired_size_in_pixel),
+      google_server_client_param_(google_server_client_param) {}
+
+FaviconServerFetcherParams::~FaviconServerFetcherParams() = default;
+
+}  // namespace favicon
diff --git a/components/favicon/core/favicon_server_fetcher_params.h b/components/favicon/core/favicon_server_fetcher_params.h
new file mode 100644
index 0000000..8484239
--- /dev/null
+++ b/components/favicon/core/favicon_server_fetcher_params.h
@@ -0,0 +1,53 @@
+// Copyright (c) 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 COMPONENTS_FAVICON_CORE_FAVICON_SERVER_FETCHER_PARAMS_H_
+#define COMPONENTS_FAVICON_CORE_FAVICON_SERVER_FETCHER_PARAMS_H_
+
+#include "components/favicon_base/favicon_util.h"
+
+class GURL;
+
+namespace favicon {
+
+class FaviconServerFetcherParams {
+ public:
+  // Platform specific constructors that set default fetch parameters. Any
+  // platform not Android nor iOS is considered desktop.
+  static std::unique_ptr<FaviconServerFetcherParams> CreateForDesktop(
+      const GURL& page_url);
+  static std::unique_ptr<FaviconServerFetcherParams> CreateForMobile(
+      const GURL& page_url,
+      int min_source_size_in_pixel,
+      int desired_size_in_pixel);
+
+  ~FaviconServerFetcherParams();
+
+  const GURL& page_url() const { return page_url_; };
+  favicon_base::IconType icon_type() const { return icon_type_; };
+  int min_source_size_in_pixel() const { return min_source_size_in_pixel_; };
+  int desired_size_in_pixel() const { return desired_size_in_pixel_; };
+  const std::string& google_server_client_param() const {
+    return google_server_client_param_;
+  };
+
+ private:
+  FaviconServerFetcherParams(const GURL& page_url,
+                             favicon_base::IconType icon_type,
+                             int min_source_size_in_pixel,
+                             int desired_size_in_pixel,
+                             const std::string& google_server_client_param);
+
+  const GURL& page_url_;
+  favicon_base::IconType icon_type_;
+  int min_source_size_in_pixel_;
+  int desired_size_in_pixel_;
+  std::string google_server_client_param_;
+
+  DISALLOW_COPY_AND_ASSIGN(FaviconServerFetcherParams);
+};
+
+}  // namespace favicon
+
+#endif  // COMPONENTS_FAVICON_CORE_FAVICON_SERVER_FETCHER_PARAMS_H_
diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc
index eac4973..4edb822b 100644
--- a/components/favicon/core/large_icon_service.cc
+++ b/components/favicon/core/large_icon_service.cc
@@ -24,6 +24,7 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
@@ -52,6 +53,8 @@
     "size=%d&min_size=%d&max_size=%d&fallback_opts=TYPE,SIZE,URL&url=%s";
 const char kGoogleServerV2RequestFormatParam[] = "request_format";
 
+const char kClientParam[] = "client=chrome";
+
 const char kCheckSeenParam[] = "check_seen=true&";
 
 const int kGoogleServerV2EnforcedMinSizeInPixel = 16;
@@ -79,10 +82,12 @@
   return page_url.ReplaceComponents(replacements);
 }
 
-GURL GetRequestUrlForGoogleServerV2(const GURL& page_url,
-                                    int min_source_size_in_pixel,
-                                    int desired_size_in_pixel,
-                                    bool may_page_url_be_private) {
+GURL GetRequestUrlForGoogleServerV2(
+    const GURL& page_url,
+    const std::string& google_server_client_param,
+    int min_source_size_in_pixel,
+    int desired_size_in_pixel,
+    bool may_page_url_be_private) {
   std::string url_format = base::GetFieldTrialParamValueByFeature(
       kLargeIconServiceFetchingFeature, kGoogleServerV2RequestFormatParam);
   double desired_to_max_size_factor = base::GetFieldTrialParamByFeatureAsDouble(
@@ -105,10 +110,13 @@
       static_cast<int>(desired_size_in_pixel * desired_to_max_size_factor);
   max_size_in_pixel = std::max(max_size_in_pixel, minimum_max_size_in_pixel);
 
-  return GURL(base::StringPrintf(
+  std::string request_url = base::StringPrintf(
       url_format.empty() ? kGoogleServerV2RequestFormat : url_format.c_str(),
       may_page_url_be_private ? kCheckSeenParam : "", desired_size_in_pixel,
-      min_source_size_in_pixel, max_size_in_pixel, page_url.spec().c_str()));
+      min_source_size_in_pixel, max_size_in_pixel, page_url.spec().c_str());
+  base::ReplaceFirstSubstringAfterOffset(
+      &request_url, 0, std::string(kClientParam), google_server_client_param);
+  return GURL(request_url);
 }
 
 bool IsDbResultAdequate(const favicon_base::FaviconRawBitmapResult& db_result,
@@ -193,8 +201,8 @@
 void FinishServerRequestAsynchronously(
     const favicon_base::GoogleFaviconServerCallback& callback,
     favicon_base::GoogleFaviconServerRequestStatus status) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, status));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(callback, status));
 }
 
 // Singleton map keyed by organization-identifying domain (excludes registrar
@@ -407,6 +415,7 @@
 void OnFetchIconFromGoogleServerComplete(
     FaviconService* favicon_service,
     const GURL& page_url,
+    favicon_base::IconType icon_type,
     const favicon_base::GoogleFaviconServerCallback& callback,
     const std::string& server_request_url,
     const gfx::Image& image,
@@ -437,8 +446,8 @@
   // expired (out-of-date), they will be refetched when we visit the original
   // page any time in the future.
   favicon_service->SetOnDemandFavicons(
-      page_url, GURL(original_icon_url), favicon_base::IconType::kTouchIcon,
-      image, base::Bind(&OnSetOnDemandFaviconComplete, callback));
+      page_url, GURL(original_icon_url), icon_type, image,
+      base::BindOnce(&OnSetOnDemandFaviconComplete, callback));
 }
 
 }  // namespace
@@ -485,13 +494,11 @@
 
 void LargeIconService::
     GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-        const GURL& page_url,
-        int min_source_size_in_pixel,
-        int desired_size_in_pixel,
+        std::unique_ptr<FaviconServerFetcherParams> params,
         bool may_page_url_be_private,
         const net::NetworkTrafficAnnotationTag& traffic_annotation,
         const favicon_base::GoogleFaviconServerCallback& callback) {
-  DCHECK_LE(0, min_source_size_in_pixel);
+  DCHECK_LE(0, params->min_source_size_in_pixel());
 
   if (net::NetworkChangeNotifier::IsOffline()) {
     // By exiting early when offline, we avoid caching the failure and thus
@@ -501,13 +508,13 @@
     return;
   }
 
-  if (!page_url.is_valid()) {
+  if (!params->page_url().is_valid()) {
     FinishServerRequestAsynchronously(
         callback, GoogleFaviconServerRequestStatus::FAILURE_TARGET_URL_INVALID);
     return;
   }
 
-  const GURL trimmed_page_url = TrimPageUrlForGoogleServer(page_url);
+  const GURL trimmed_page_url = TrimPageUrlForGoogleServer(params->page_url());
   if (!trimmed_page_url.is_valid()) {
     FinishServerRequestAsynchronously(
         callback, GoogleFaviconServerRequestStatus::FAILURE_TARGET_URL_SKIPPED);
@@ -515,7 +522,8 @@
   }
 
   const GURL server_request_url = GetRequestUrlForGoogleServerV2(
-      trimmed_page_url, min_source_size_in_pixel, desired_size_in_pixel,
+      trimmed_page_url, params->google_server_client_param(),
+      params->min_source_size_in_pixel(), params->desired_size_in_pixel(),
       may_page_url_be_private);
   if (!server_request_url.is_valid()) {
     FinishServerRequestAsynchronously(
@@ -532,10 +540,11 @@
   }
 
   favicon_service_->CanSetOnDemandFavicons(
-      page_url, favicon_base::IconType::kTouchIcon,
+      params->page_url(), params->icon_type(),
       base::BindOnce(&LargeIconService::OnCanSetOnDemandFaviconComplete,
                      weak_ptr_factory_.GetWeakPtr(), server_request_url,
-                     page_url, traffic_annotation, callback));
+                     params->page_url(), params->icon_type(),
+                     traffic_annotation, callback));
 }
 
 void LargeIconService::TouchIconFromGoogleServer(const GURL& icon_url) {
@@ -591,6 +600,7 @@
 void LargeIconService::OnCanSetOnDemandFaviconComplete(
     const GURL& server_request_url,
     const GURL& page_url,
+    favicon_base::IconType icon_type,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     const favicon_base::GoogleFaviconServerCallback& callback,
     bool can_set_on_demand_favicon) {
@@ -603,8 +613,8 @@
       data_use_measurement::DataUseUserData::LARGE_ICON_SERVICE);
   image_fetcher_->FetchImage(
       server_request_url.spec(), server_request_url,
-      base::BindRepeating(&OnFetchIconFromGoogleServerComplete,
-                          favicon_service_, page_url, callback),
+      base::BindOnce(&OnFetchIconFromGoogleServerComplete, favicon_service_,
+                     page_url, icon_type, callback),
       traffic_annotation);
 }
 
diff --git a/components/favicon/core/large_icon_service.h b/components/favicon/core/large_icon_service.h
index 96bd8b1..1d1a32c 100644
--- a/components/favicon/core/large_icon_service.h
+++ b/components/favicon/core/large_icon_service.h
@@ -26,6 +26,7 @@
 namespace favicon {
 
 class FaviconService;
+class FaviconServerFetcherParams;
 
 // The large icon service provides methods to access large icons. It relies on
 // the favicon service.
@@ -93,9 +94,7 @@
   // TODO(jkrcal): It is not clear from the name of this function, that it
   // actually adds the icon to the local cache. Maybe "StoreLargeIcon..."?
   void GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-      const GURL& page_url,
-      int min_source_size_in_pixel,
-      int desired_size_in_pixel,
+      std::unique_ptr<FaviconServerFetcherParams> params,
       bool may_page_url_be_private,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       const favicon_base::GoogleFaviconServerCallback& callback);
@@ -125,6 +124,7 @@
   void OnCanSetOnDemandFaviconComplete(
       const GURL& server_request_url,
       const GURL& page_url,
+      favicon_base::IconType icon_type,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       const favicon_base::GoogleFaviconServerCallback& callback,
       bool can_set_on_demand_favicon);
diff --git a/components/favicon/core/large_icon_service_unittest.cc b/components/favicon/core/large_icon_service_unittest.cc
index 7007c78..c563b847 100644
--- a/components/favicon/core/large_icon_service_unittest.cc
+++ b/components/favicon/core/large_icon_service_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/favicon/core/favicon_client.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
@@ -29,6 +30,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/layout.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
@@ -125,7 +127,10 @@
   LargeIconServiceTest()
       : mock_image_fetcher_(new NiceMock<MockImageFetcher>()),
         large_icon_service_(&mock_favicon_service_,
-                            base::WrapUnique(mock_image_fetcher_)) {}
+                            base::WrapUnique(mock_image_fetcher_)) {
+    scoped_set_supported_scale_factors_.reset(
+        new ui::test::ScopedSetSupportedScaleFactors({ui::SCALE_FACTOR_200P}));
+  }
 
   ~LargeIconServiceTest() override {}
 
@@ -135,6 +140,8 @@
   testing::NiceMock<MockFaviconService> mock_favicon_service_;
   LargeIconService large_icon_service_;
   base::HistogramTester histogram_tester_;
+  std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors>
+      scoped_set_supported_scale_factors_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest);
@@ -164,9 +171,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback,
               Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
@@ -175,6 +185,42 @@
       "Favicons.LargeIconService.DownloadedSize", 64, /*expected_count=*/1);
 }
 
+TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerForDesktop) {
+  const GURL kExpectedServerUrl(
+      "https://t0.gstatic.com/faviconV2?client=chrome_desktop"
+      "&drop_404_icon=true&check_seen=true&size=32&min_size=32&max_size=256"
+      "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/");
+
+  EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
+  EXPECT_CALL(mock_favicon_service_,
+              CanSetOnDemandFavicons(GURL(kDummyUrl),
+                                     favicon_base::IconType::kFavicon, _))
+      .WillOnce(PostBoolReplyToArg2(true));
+
+  base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
+  EXPECT_CALL(*mock_image_fetcher_,
+              FetchImageAndData_(_, kExpectedServerUrl, _, _, _))
+      .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap(
+          CreateTestSkBitmap(32, 32, kTestColor))));
+  EXPECT_CALL(mock_favicon_service_,
+              SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl,
+                                  favicon_base::IconType::kFavicon, _, _))
+      .WillOnce(PostBoolReplyToArg4(true));
+
+  large_icon_service_
+      .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
+          favicon::FaviconServerFetcherParams::CreateForDesktop(
+              GURL(kDummyUrl)),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
+
+  EXPECT_CALL(callback,
+              Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
+  scoped_task_environment_.RunUntilIdle();
+  histogram_tester_.ExpectUniqueSample(
+      "Favicons.LargeIconService.DownloadedSize", 32, /*expected_count=*/1);
+}
+
 TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithCustomUrl) {
   variations::testing::VariationParamsManager variation_params(
       "LargeIconServiceFetching",
@@ -206,9 +252,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback,
               Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
@@ -243,9 +292,12 @@
   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback,
               Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS));
@@ -274,9 +326,11 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrlWithQuery), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS,
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrlWithQuery),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
           favicon_base::GoogleFaviconServerCallback());
 
   scoped_task_environment_.RunUntilIdle();
@@ -298,9 +352,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/false,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/false, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_CONNECTION_ERROR));
@@ -316,9 +373,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyFtpUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyFtpUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_TARGET_URL_SKIPPED));
@@ -337,9 +397,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyInvalidUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyInvalidUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_TARGET_URL_INVALID));
@@ -371,9 +434,12 @@
 
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_CONNECTION_ERROR));
@@ -400,9 +466,12 @@
   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_HTTP_ERROR_CACHED));
@@ -426,9 +495,12 @@
   base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback;
   large_icon_service_
       .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          GURL(kDummyUrl), /*min_source_size_in_pixel=*/42,
-          /*desired_size_in_pixel=*/61, /*may_page_url_be_private=*/true,
-          TRAFFIC_ANNOTATION_FOR_TESTS, callback.Get());
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              GURL(kDummyUrl),
+              /*min_source_size_in_pixel=*/42,
+              /*desired_size_in_pixel=*/61),
+          /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
+          callback.Get());
 
   EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus::
                                 FAILURE_ICON_EXISTS_IN_DB));
diff --git a/components/mirroring/service/BUILD.gn b/components/mirroring/service/BUILD.gn
index 6aea231d..2a91cfff 100644
--- a/components/mirroring/service/BUILD.gn
+++ b/components/mirroring/service/BUILD.gn
@@ -41,6 +41,8 @@
     "value_util.h",
     "video_capture_client.cc",
     "video_capture_client.h",
+    "wifi_status_monitor.cc",
+    "wifi_status_monitor.h",
   ]
 
   public_deps = [
@@ -77,6 +79,7 @@
     "session_unittest.cc",
     "udp_socket_client_unittest.cc",
     "video_capture_client_unittest.cc",
+    "wifi_status_monitor_unittest.cc",
   ]
 
   deps = [
diff --git a/components/mirroring/service/wifi_status_monitor.cc b/components/mirroring/service/wifi_status_monitor.cc
new file mode 100644
index 0000000..f7a57a48
--- /dev/null
+++ b/components/mirroring/service/wifi_status_monitor.cc
@@ -0,0 +1,77 @@
+// 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 "components/mirroring/service/wifi_status_monitor.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "components/mirroring/service/message_dispatcher.h"
+
+namespace mirroring {
+
+namespace {
+
+// The interval to query the status.
+constexpr base::TimeDelta kQueryInterval = base::TimeDelta::FromMinutes(2);
+
+// The maximum number of recent status to be kept.
+constexpr int kMaxRecords = 30;
+
+}  // namespace
+
+WifiStatusMonitor::WifiStatusMonitor(int32_t session_id,
+                                     MessageDispatcher* message_dispatcher)
+    : session_id_(session_id), message_dispatcher_(message_dispatcher) {
+  DCHECK(message_dispatcher_);
+  message_dispatcher_->Subscribe(
+      ResponseType::STATUS_RESPONSE,
+      base::BindRepeating(&WifiStatusMonitor::RecordStatus,
+                          base::Unretained(this)));
+  query_timer_.Start(FROM_HERE, kQueryInterval,
+                     base::BindRepeating(&WifiStatusMonitor::QueryStatus,
+                                         base::Unretained(this)));
+  QueryStatus();
+}
+
+WifiStatusMonitor::~WifiStatusMonitor() {
+  message_dispatcher_->Unsubscribe(ResponseType::STATUS_RESPONSE);
+}
+
+std::vector<WifiStatus> WifiStatusMonitor::GetRecentValues() {
+  std::vector<WifiStatus> recent_status(recent_status_.begin(),
+                                        recent_status_.end());
+  recent_status_.clear();
+  return recent_status;
+}
+
+void WifiStatusMonitor::QueryStatus() {
+  base::Value query(base::Value::Type::DICTIONARY);
+  query.SetKey("type", base::Value("GET_STATUS"));
+  query.SetKey("sessionId", base::Value(session_id_));
+  query.SetKey("seqNum", base::Value(message_dispatcher_->GetNextSeqNumber()));
+  base::Value::ListStorage status;
+  status.emplace_back(base::Value("wifiSnr"));
+  status.emplace_back(base::Value("wifiSpeed"));
+  query.SetKey("get_status", base::Value(status));
+  CastMessage get_status_message;
+  get_status_message.message_namespace = kWebRtcNamespace;
+  get_status_message.data = std::move(query);
+  message_dispatcher_->SendOutboundMessage(get_status_message);
+}
+
+void WifiStatusMonitor::RecordStatus(const ReceiverResponse& response) {
+  if (!response.status || response.status->wifi_speed.size() != 4)
+    return;
+  if (recent_status_.size() == kMaxRecords)
+    recent_status_.pop_front();
+  WifiStatus received_status;
+  received_status.snr = response.status->wifi_snr;
+  // Only records the current speed.
+  received_status.speed = response.status->wifi_speed[3];
+  received_status.timestamp = base::Time::Now();
+  recent_status_.emplace_back(received_status);
+}
+
+}  // namespace mirroring
diff --git a/components/mirroring/service/wifi_status_monitor.h b/components/mirroring/service/wifi_status_monitor.h
new file mode 100644
index 0000000..ac348ce
--- /dev/null
+++ b/components/mirroring/service/wifi_status_monitor.h
@@ -0,0 +1,61 @@
+// 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 COMPONENTS_MIRRORING_SERVICE_WIFI_STATUS_MONITOR_H_
+#define COMPONENTS_MIRRORING_SERVICE_WIFI_STATUS_MONITOR_H_
+
+#include <vector>
+
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace mirroring {
+
+class MessageDispatcher;
+struct ReceiverResponse;
+
+struct WifiStatus {
+  double snr;
+  int32_t speed;         // The current WiFi speed.
+  base::Time timestamp;  // Recording time of this status.
+};
+
+// Periodically sends requests to the Cast device for WiFi network status
+// updates, processes responses, and maintains a recent history of data points.
+// This data can be included in feedback logs to help identify and diagnose
+// issues related to lousy network performance.
+class WifiStatusMonitor {
+ public:
+  // |message_dispatcher| must keep alive during the lifetime of this class.
+  WifiStatusMonitor(int32_t session_id, MessageDispatcher* message_dispatcher);
+  ~WifiStatusMonitor();
+
+  // Gets the recorded status and clear |recent_status_|.
+  std::vector<WifiStatus> GetRecentValues();
+
+  // Sends GET_STATUS message to receiver.
+  void QueryStatus();
+
+  // Callback for the STATUS_RESPONSE message. Records the WiFi status reported
+  // by receiver.
+  void RecordStatus(const ReceiverResponse& response);
+
+ private:
+  const int32_t session_id_;
+
+  MessageDispatcher* const message_dispatcher_;  // Outlives this class.
+
+  base::RepeatingTimer query_timer_;
+
+  // Stores the recent status. Will be reset when GetRecentValues() is called.
+  base::circular_deque<WifiStatus> recent_status_;
+
+  DISALLOW_COPY_AND_ASSIGN(WifiStatusMonitor);
+};
+
+}  // namespace mirroring
+
+#endif  // COMPONENTS_MIRRORING_SERVICE_WIFI_STATUS_MONITOR_H_
diff --git a/components/mirroring/service/wifi_status_monitor_unittest.cc b/components/mirroring/service/wifi_status_monitor_unittest.cc
new file mode 100644
index 0000000..26e53f0
--- /dev/null
+++ b/components/mirroring/service/wifi_status_monitor_unittest.cc
@@ -0,0 +1,173 @@
+// 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 "components/mirroring/service/wifi_status_monitor.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/mirroring/service/message_dispatcher.h"
+#include "components/mirroring/service/value_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace mirroring {
+
+namespace {
+
+bool IsNullMessage(const CastMessage& message) {
+  return message.message_namespace.empty() && message.data.is_none();
+}
+
+std::string GetMessageType(const CastMessage& message) {
+  std::string type;
+  if (message.data.is_none())
+    return type;
+  EXPECT_TRUE(GetString(message.data, "type", &type));
+  return type;
+}
+
+void VerifyRecordedStatus(const std::vector<WifiStatus> recorded_status,
+                          double starting_snr,
+                          int starting_speed,
+                          int num_of_responses) {
+  EXPECT_EQ(num_of_responses, static_cast<int>(recorded_status.size()));
+  for (int i = 0; i < num_of_responses; ++i) {
+    EXPECT_EQ(starting_snr + i, recorded_status[i].snr);
+    EXPECT_EQ(starting_speed + i, recorded_status[i].speed);
+  }
+}
+
+}  // namespace
+
+class WifiStatusMonitorTest : public CastMessageChannel,
+                              public ::testing::Test {
+ public:
+  WifiStatusMonitorTest() : message_dispatcher_(this, error_callback_.Get()) {}
+
+  ~WifiStatusMonitorTest() override {}
+
+  // CastMessageChannel implementation. For outbound messages.
+  void Send(const CastMessage& message) override {
+    last_outbound_message_.message_namespace = message.message_namespace;
+    last_outbound_message_.data = message.data.Clone();
+  }
+
+ protected:
+  // Generates and sends |num_of_responses| responses.
+  void SendStatusResponses(double starting_snr,
+                           int starting_speed,
+                           int num_of_responses) {
+    for (int i = 0; i < num_of_responses; ++i) {
+      const std::string response =
+          "{\"seqNum\":" +
+          std::to_string(message_dispatcher_.GetNextSeqNumber()) +
+          ","
+          "\"type\": \"STATUS_RESPONSE\","
+          "\"result\": \"ok\","
+          "\"status\": {"
+          "\"wifiSnr\":" +
+          std::to_string(starting_snr + i) +
+          ","
+          "\"wifiSpeed\": [1234, 5678, 3000, " +
+          std::to_string(starting_speed + i) +
+          "],"
+          "\"wifiFcsError\": [12, 13, 12, 12]}"  // This will be ignored.
+          "}";
+      SendInboundMessage(response);
+    }
+  }
+
+  // Sends an inbound message to |message_dispatcher|.
+  void SendInboundMessage(const std::string& response) {
+    std::unique_ptr<base::Value> value = base::JSONReader::Read(response);
+    ASSERT_TRUE(value);
+    CastMessage message;
+    message.message_namespace = kWebRtcNamespace;
+    message.data = base::Value::FromUniquePtrValue(std::move(value));
+    static_cast<CastMessageChannel*>(&message_dispatcher_)->Send(message);
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  // Creates a WifiStatusMonitor and start monitoring the status.
+  std::unique_ptr<WifiStatusMonitor> StartMonitoring() {
+    EXPECT_TRUE(IsNullMessage(last_outbound_message_));
+    EXPECT_CALL(error_callback_, Run(_)).Times(0);
+    auto status_monitor =
+        std::make_unique<WifiStatusMonitor>(123, &message_dispatcher_);
+    scoped_task_environment_.RunUntilIdle();
+    // Expect to receive request to send GET_STATUS message when create a
+    // WifiStatusMonitor.
+    EXPECT_EQ(kWebRtcNamespace, last_outbound_message_.message_namespace);
+    EXPECT_EQ("GET_STATUS", GetMessageType(last_outbound_message_));
+    // Clear the old outbound message.
+    last_outbound_message_.message_namespace.clear();
+    last_outbound_message_.data = base::Value();
+    EXPECT_TRUE(IsNullMessage(last_outbound_message_));
+    return status_monitor;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::MockCallback<MessageDispatcher::ErrorCallback> error_callback_;
+  MessageDispatcher message_dispatcher_;
+  CastMessage last_outbound_message_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WifiStatusMonitorTest);
+};
+
+TEST_F(WifiStatusMonitorTest, QueryStatusAndRecordResponse) {
+  std::unique_ptr<WifiStatusMonitor> status_monitor = StartMonitoring();
+
+  // Send two responses and verify the data are stored.
+  SendStatusResponses(36.7, 3001, 2);
+  std::vector<WifiStatus> recent_status = status_monitor->GetRecentValues();
+  VerifyRecordedStatus(recent_status, 36.7, 3001, 2);
+
+  // There should be no further status stored.
+  recent_status = status_monitor->GetRecentValues();
+  EXPECT_TRUE(recent_status.empty());
+
+  // Sends more than the maximum number (30) of records that can be stored.
+  SendStatusResponses(36.7, 3001, 40);
+  // Expect that only the recent 30 records were stored.
+  recent_status = status_monitor->GetRecentValues();
+  VerifyRecordedStatus(recent_status, 46.7, 3011, 30);
+}
+
+TEST_F(WifiStatusMonitorTest, IgnoreMalformedStatusMessage) {
+  std::unique_ptr<WifiStatusMonitor> status_monitor = StartMonitoring();
+
+  // Sends a response with incomplete wifiSpeed data and expects it is ignored.
+  const std::string response1 =
+      "{\"seqNum\": 123,"
+      "\"type\": \"STATUS_RESPONSE\","
+      "\"result\": \"ok\","
+      "\"status\": {"
+      "\"wifiSnr\": 32,"
+      "\"wifiSpeed\": [1234, 5678, 3000],"
+      "\"wifiFcsError\": [12, 13, 12, 12]}"
+      "}";
+  SendInboundMessage(response1);
+  std::vector<WifiStatus> recent_status = status_monitor->GetRecentValues();
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_TRUE(recent_status.empty());
+
+  // Sends a response with null status field and expects it is ignored.
+  const std::string response2 =
+      "{\"seqNum\": 123,"
+      "\"type\": \"STATUS_RESPONSE\","
+      "\"status\": null}";
+  SendInboundMessage(response2);
+  recent_status = status_monitor->GetRecentValues();
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_TRUE(recent_status.empty());
+}
+
+}  // namespace mirroring
diff --git a/components/net_log/BUILD.gn b/components/net_log/BUILD.gn
index 133894e..dc1750e 100644
--- a/components/net_log/BUILD.gn
+++ b/components/net_log/BUILD.gn
@@ -17,6 +17,7 @@
     "//components/data_reduction_proxy/core/common",
     "//components/version_info",
     "//net",
+    "//services/network/public/mojom",
   ]
 }
 
@@ -31,6 +32,9 @@
     "//base/test:test_support",
     "//net",
     "//net:test_support",
+    "//services/network:network_service",
+    "//services/network/public/cpp",
+    "//services/network/public/cpp:cpp_base",
     "//testing/gtest",
   ]
 }
diff --git a/components/net_log/DEPS b/components/net_log/DEPS
index efca3dd..4d7f1c7 100644
--- a/components/net_log/DEPS
+++ b/components/net_log/DEPS
@@ -2,4 +2,5 @@
   "+components/data_reduction_proxy/core/common",
   "+components/version_info",
   "+net",
+  "+services/network",
 ]
diff --git a/components/net_log/chrome_net_log.cc b/components/net_log/chrome_net_log.cc
index a799eee..878b964 100644
--- a/components/net_log/chrome_net_log.cc
+++ b/components/net_log/chrome_net_log.cc
@@ -49,7 +49,7 @@
 
 NetExportFileWriter* ChromeNetLog::net_export_file_writer() {
   if (!net_export_file_writer_)
-    net_export_file_writer_ = base::WrapUnique(new NetExportFileWriter(this));
+    net_export_file_writer_ = base::WrapUnique(new NetExportFileWriter());
   return net_export_file_writer_.get();
 }
 
@@ -61,6 +61,18 @@
       net::GetNetConstants();
   DCHECK(constants_dict);
 
+  auto platform_dict =
+      GetPlatformConstants(command_line_string, channel_string);
+  if (platform_dict)
+    constants_dict->MergeDictionary(platform_dict.get());
+  return constants_dict;
+}
+
+std::unique_ptr<base::DictionaryValue> ChromeNetLog::GetPlatformConstants(
+    const base::CommandLine::StringType& command_line_string,
+    const std::string& channel_string) {
+  auto constants_dict = std::make_unique<base::DictionaryValue>();
+
   // Add a dictionary with the version of the client and its command line
   // arguments.
   auto dict = std::make_unique<base::DictionaryValue>();
@@ -84,7 +96,7 @@
   data_reduction_proxy::DataReductionProxyEventStore::AddConstants(
       constants_dict.get());
 
-  return std::move(constants_dict);
+  return constants_dict;
 }
 
 void ChromeNetLog::ShutDownBeforeTaskScheduler() {
diff --git a/components/net_log/chrome_net_log.h b/components/net_log/chrome_net_log.h
index cfa0298..ead1479 100644
--- a/components/net_log/chrome_net_log.h
+++ b/components/net_log/chrome_net_log.h
@@ -56,6 +56,12 @@
       const base::CommandLine::StringType& command_line_string,
       const std::string& channel_string);
 
+  // Returns only platform-specific constants. This doesn't include the net/
+  // baseline, only Chrome-specific platform information.
+  static std::unique_ptr<base::DictionaryValue> GetPlatformConstants(
+      const base::CommandLine::StringType& command_line_string,
+      const std::string& channel_string);
+
   // Notify the ChromeNetLog that things are shutting-down.
   //
   // If ChromeNetLog does not outlive the TaskScheduler, there is no need to
diff --git a/components/net_log/net_export_file_writer.cc b/components/net_log/net_export_file_writer.cc
index 87252ef9..6f3597f 100644
--- a/components/net_log/net_export_file_writer.cc
+++ b/components/net_log/net_export_file_writer.cc
@@ -14,20 +14,12 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/net_log/chrome_net_log.h"
-#include "net/log/file_net_log_observer.h"
-#include "net/log/net_log_util.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace net {
-class URLRequestContext;
-}
 
 namespace net_log {
 
@@ -61,31 +53,6 @@
   return results;
 }
 
-// Generates net log entries for ongoing events from |context_getters| and
-// adds them to |observer|.
-void CreateNetLogEntriesForActiveObjects(
-    const NetExportFileWriter::URLRequestContextGetterList& context_getters,
-    net::NetLog::ThreadSafeObserver* observer) {
-  std::set<net::URLRequestContext*> contexts;
-  for (const auto& getter : context_getters) {
-    DCHECK(getter->GetNetworkTaskRunner()->BelongsToCurrentThread());
-    contexts.insert(getter->GetURLRequestContext());
-  }
-  net::CreateNetLogEntriesForActiveObjects(contexts, observer);
-}
-
-// Adds net info from net::GetNetInfo() to |polled_data|.
-std::unique_ptr<base::DictionaryValue> AddNetInfo(
-    scoped_refptr<net::URLRequestContextGetter> context_getter,
-    std::unique_ptr<base::DictionaryValue> polled_data) {
-  DCHECK(context_getter);
-  std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
-      context_getter->GetURLRequestContext(), net::NET_INFO_ALL_SOURCES);
-  if (polled_data)
-    net_info->MergeDictionary(polled_data.get());
-  return net_info;
-}
-
 // If running on a POSIX OS, this will attempt to set all the permission flags
 // of the file at |path| to 1. Will return |path| on success and the empty path
 // on failure.
@@ -114,18 +81,19 @@
 
 }  // namespace
 
-NetExportFileWriter::NetExportFileWriter(ChromeNetLog* chrome_net_log)
+NetExportFileWriter::NetExportFileWriter()
     : state_(STATE_UNINITIALIZED),
       log_exists_(false),
       log_capture_mode_known_(false),
       log_capture_mode_(net::NetLogCaptureMode::Default()),
-      chrome_net_log_(chrome_net_log),
       default_log_base_dir_getter_(base::Bind(&base::GetTempDir)),
       weak_ptr_factory_(this) {}
 
 NetExportFileWriter::~NetExportFileWriter() {
-  if (file_net_log_observer_)
-    file_net_log_observer_->StopObserving(nullptr, base::Closure());
+  if (net_log_exporter_) {
+    net_log_exporter_->Stop(base::Value(base::Value::Type::DICTIONARY),
+                            base::DoNothing());
+  }
 }
 
 void NetExportFileWriter::AddObserver(StateObserver* observer) {
@@ -138,15 +106,10 @@
   state_observer_list_.RemoveObserver(observer);
 }
 
-void NetExportFileWriter::Initialize(
-    scoped_refptr<base::TaskRunner> net_task_runner) {
+void NetExportFileWriter::Initialize() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(net_task_runner);
 
   file_task_runner_ = CreateFileTaskRunner();
-  if (net_task_runner_)
-    DCHECK_EQ(net_task_runner_, net_task_runner);
-  net_task_runner_ = net_task_runner;
 
   if (state_ != STATE_UNINITIALIZED)
     return;
@@ -168,7 +131,7 @@
     uint64_t max_file_size,
     const base::CommandLine::StringType& command_line_string,
     const std::string& channel_string,
-    const URLRequestContextGetterList& context_getters) {
+    network::mojom::NetworkContext* network_context) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(file_task_runner_);
 
@@ -184,41 +147,70 @@
 
   NotifyStateObserversAsync();
 
-  std::unique_ptr<base::Value> constants(
-      ChromeNetLog::GetConstants(command_line_string, channel_string));
+  network_context->CreateNetLogExporter(mojo::MakeRequest(&net_log_exporter_));
+  base::Value custom_constants = base::Value::FromUniquePtrValue(
+      ChromeNetLog::GetPlatformConstants(command_line_string, channel_string));
 
-  file_net_log_observer_ = net::FileNetLogObserver::CreateBounded(
-      log_path_, max_file_size, std::move(constants));
-
-  net_task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&CreateNetLogEntriesForActiveObjects, context_getters,
-                 base::Unretained(file_net_log_observer_.get())),
-      base::Bind(
-          &NetExportFileWriter::StartNetLogAfterCreateEntriesForActiveObjects,
-          weak_ptr_factory_.GetWeakPtr(), capture_mode));
+  base::PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&NetExportFileWriter::CreateOutputFile, log_path_),
+      base::BindOnce(&NetExportFileWriter::StartNetLogAfterCreateFile,
+                     weak_ptr_factory_.GetWeakPtr(), capture_mode,
+                     max_file_size, std::move(custom_constants)));
 }
 
-void NetExportFileWriter::StartNetLogAfterCreateEntriesForActiveObjects(
-    net::NetLogCaptureMode capture_mode) {
+void NetExportFileWriter::StartNetLogAfterCreateFile(
+    net::NetLogCaptureMode capture_mode,
+    uint64_t max_file_size,
+    base::Value custom_constants,
+    base::File output_file) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_EQ(STATE_STARTING_LOG, state_);
 
-  state_ = STATE_LOGGING;
-  log_exists_ = true;
-  log_capture_mode_known_ = true;
-  log_capture_mode_ = capture_mode;
+  // TODO(morlovich): Communicate file open trouble better
+  // (https://crbug.com/838977)
+  if (!output_file.IsValid()) {
+    ResetExporterThenSetStateNotLogging();
+    return;
+  }
 
-  NotifyStateObservers();
+  network::mojom::NetLogExporter_CaptureMode rpc_capture_mode =
+      network::mojom::NetLogExporter::CaptureMode::DEFAULT;
+  if (capture_mode.include_socket_bytes()) {
+    rpc_capture_mode =
+        network::mojom::NetLogExporter::CaptureMode::INCLUDE_SOCKET_BYTES;
+  } else if (capture_mode.include_cookies_and_credentials()) {
+    rpc_capture_mode = network::mojom::NetLogExporter::CaptureMode::
+        INCLUDE_COOKIES_AND_CREDENTIALS;
+  }
 
-  file_net_log_observer_->StartObserving(chrome_net_log_, capture_mode);
+  // base::Unretained(this) is safe here since |net_log_exporter_| is owned by
+  // |this| and is a mojo InterfacePtr, which guarantees callback cancellation
+  // upon its destruction.
+  net_log_exporter_->Start(
+      std::move(output_file), std::move(custom_constants), rpc_capture_mode,
+      max_file_size,
+      base::BindOnce(&NetExportFileWriter::OnStartResult,
+                     base::Unretained(this), capture_mode));
+}
+
+void NetExportFileWriter::OnStartResult(net::NetLogCaptureMode capture_mode,
+                                        int result) {
+  if (result == net::OK) {
+    state_ = STATE_LOGGING;
+    log_exists_ = true;
+    log_capture_mode_known_ = true;
+    log_capture_mode_ = capture_mode;
+
+    NotifyStateObservers();
+  } else {
+    ResetExporterThenSetStateNotLogging();
+  }
 }
 
 void NetExportFileWriter::StopNetLog(
-    std::unique_ptr<base::DictionaryValue> polled_data,
-    scoped_refptr<net::URLRequestContextGetter> context_getter) {
+    std::unique_ptr<base::DictionaryValue> polled_data) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(net_task_runner_);
 
   if (state_ != STATE_LOGGING)
     return;
@@ -227,15 +219,19 @@
 
   NotifyStateObserversAsync();
 
-  if (context_getter) {
-    base::PostTaskAndReplyWithResult(
-        net_task_runner_.get(), FROM_HERE,
-        base::BindOnce(&AddNetInfo, context_getter, std::move(polled_data)),
-        base::BindOnce(&NetExportFileWriter::StopNetLogAfterAddNetInfo,
-                       weak_ptr_factory_.GetWeakPtr()));
-  } else {
-    StopNetLogAfterAddNetInfo(std::move(polled_data));
-  }
+  base::Value polled_data_value(base::Value::Type::DICTIONARY);
+  if (polled_data)
+    polled_data_value = base::Value::FromUniquePtrValue(std::move(polled_data));
+  // base::Unretained(this) is safe here since |net_log_exporter_| is owned by
+  // |this| and is a mojo InterfacePtr, which guarantees callback cancellation
+  // upon its destruction.
+  net_log_exporter_->Stop(std::move(polled_data_value),
+                          base::BindOnce(&NetExportFileWriter::OnStopResult,
+                                         base::Unretained(this)));
+}
+
+void NetExportFileWriter::OnStopResult(int result) {
+  ResetExporterThenSetStateNotLogging();
 }
 
 std::unique_ptr<base::DictionaryValue> NetExportFileWriter::GetState() const {
@@ -352,23 +348,17 @@
   NotifyStateObservers();
 }
 
-void NetExportFileWriter::StopNetLogAfterAddNetInfo(
-    std::unique_ptr<base::DictionaryValue> polled_data) {
+void NetExportFileWriter::ResetExporterThenSetStateNotLogging() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(STATE_STOPPING_LOG, state_);
-
-  file_net_log_observer_->StopObserving(
-      std::move(polled_data),
-      base::Bind(&NetExportFileWriter::ResetObserverThenSetStateNotLogging,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void NetExportFileWriter::ResetObserverThenSetStateNotLogging() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  file_net_log_observer_.reset();
+  net_log_exporter_.reset();
   state_ = STATE_NOT_LOGGING;
 
   NotifyStateObservers();
 }
 
+base::File NetExportFileWriter::CreateOutputFile(base::FilePath path) {
+  return base::File(path,
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+}
+
 }  // namespace net_log
diff --git a/components/net_log/net_export_file_writer.h b/components/net_log/net_export_file_writer.h
index f0453320..b734e5d 100644
--- a/components/net_log/net_export_file_writer.h
+++ b/components/net_log/net_export_file_writer.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -17,17 +18,19 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
-#include "net/log/file_net_log_observer.h"
+#include "base/values.h"
 #include "net/log/net_log_capture_mode.h"
+#include "services/network/public/mojom/network_service.mojom.h"
 
 namespace base {
-class DictionaryValue;
 class TaskRunner;
 }  // namespace base
 
-namespace net {
-class URLRequestContextGetter;
-}  // namespace net
+namespace network {
+namespace mojom {
+class NetworkContext;
+}
+}  // namespace network
 
 namespace net_log {
 
@@ -37,10 +40,10 @@
 // It's a singleton that acts as the interface to all NetExportMessageHandlers
 // which can tell it to start or stop logging in response to user actions from
 // net-export UIs. Because it's a singleton, the logging state can be shared
-// between multiple instances of the net-export UI. Internally, it manages an
-// instance of net::FileNetLogObserver and handles the attaching/detaching of it
-// to the ChromeNetLog. This class is used by the iOS and non-iOS
-// implementations of net-export.
+// between multiple instances of the net-export UI. Internally, it manages a
+// pipe to an instance of network::NetLogExporter and handles the
+// attaching/detaching of it to the NetLog. This class is used by the iOS and
+// non-iOS implementations of net-export.
 //
 // NetExportFileWriter maintains the current logging state using the members
 // |state_|, |log_exists_|, |log_capture_mode_known_|, |log_capture_mode_|.
@@ -52,11 +55,12 @@
 //
 // This class is created and destroyed on the UI thread, and all public entry
 // points are to be called on the UI thread. Internally, the class may run some
-// code on the |file_task_runner_| and |net_task_runner_|.
+// code on the |file_task_runner_|.
 class NetExportFileWriter {
  public:
   // Special value meaning "can use an unlimited number of bytes".
-  static constexpr uint64_t kNoLimit = net::FileNetLogObserver::kNoLimit;
+  static constexpr uint64_t kNoLimit =
+      network::mojom::NetLogExporter::kUnlimitedFileSize;
 
   // The observer interface to be implemented by code that wishes to be notified
   // of NetExportFileWriter's state changes.
@@ -75,8 +79,6 @@
 
   using FilePathCallback = base::Callback<void(const base::FilePath&)>;
   using DirectoryGetter = base::Callback<bool(base::FilePath*)>;
-  using URLRequestContextGetterList =
-      std::vector<scoped_refptr<net::URLRequestContextGetter>>;
 
   ~NetExportFileWriter();
 
@@ -90,11 +92,9 @@
   // Detaches a StateObserver.
   void RemoveObserver(StateObserver* observer);
 
-  // Initializes NetExportFileWriter if not initialized and sets the network
-  // task runner (used by NetExportFileWriter when querying network state).
-  // This task runner must not be changed once set. Calling this function again
-  // with the same parameter is OK.
-  void Initialize(scoped_refptr<base::TaskRunner> net_task_runner);
+  // Initializes NetExportFileWriter if not initialized. Calling this function
+  // again is OK.
+  void Initialize();
 
   // Starts collecting NetLog data into the file at |log_path|. If |log_path| is
   // empty, the default log path is used. If NetExportFileWriter is already
@@ -103,30 +103,21 @@
   // |max_file_size| places a bound on how large the log file can grow. To make
   // it grow unboundedly pass kNoLimit.
   //
-  // |context_getters| is an optional list of URLRequestContextGetters used only
-  // to add log entries for ongoing events when logging starts. They are not
-  // used for retrieving polled data. All the contexts must be bound to the same
-  // thread.
+  // |network_context| will be used to append net info (from net::GetInfo())
+  // at end of logging once StopNetLog is called.
   void StartNetLog(const base::FilePath& log_path,
                    net::NetLogCaptureMode capture_mode,
                    uint64_t max_file_size,
                    const base::CommandLine::StringType& command_line_string,
                    const std::string& channel_string,
-                   const URLRequestContextGetterList& context_getters);
+                   network::mojom::NetworkContext* network_context);
 
   // Stops collecting NetLog data into the file. It is a no-op if
   // NetExportFileWriter is currently not logging.
   //
   // |polled_data| is a JSON dictionary that will be appended to the end of the
   // log; it's for adding additional info to the log that aren't events.
-  // If |context_getter| is not null, then  StopNetLog() will automatically
-  // append net info (from net::GetNetInfo() retrieved using |context_getter|)
-  // to |polled_data|.
-  // Note that StopNetLog() accepts (optionally) only one context getter for
-  // retrieving net polled data as opposed to StartNetLog() which accepts zero
-  // or more context getters for retrieving ongoing net events.
-  void StopNetLog(std::unique_ptr<base::DictionaryValue> polled_data,
-                  scoped_refptr<net::URLRequestContextGetter> context_getter);
+  void StopNetLog(std::unique_ptr<base::DictionaryValue> polled_data);
 
   // Creates a DictionaryValue summary of the state of the NetExportFileWriter
   std::unique_ptr<base::DictionaryValue> GetState() const;
@@ -156,7 +147,7 @@
  protected:
   // Constructs a NetExportFileWriter. Only one instance is created in browser
   // process.
-  explicit NetExportFileWriter(ChromeNetLog* chrome_net_log);
+  NetExportFileWriter();
 
  private:
   friend class ChromeNetLog;
@@ -189,30 +180,30 @@
       const DefaultLogPathResults& set_up_default_log_path_results);
 
   // Called internally by StartNetLog(). Contains tasks to be done to start
-  // logging after net log entries for ongoing events are added to the log from
-  // the |net_task_runner_|.
-  void StartNetLogAfterCreateEntriesForActiveObjects(
-      net::NetLogCaptureMode capture_mode);
+  // logging after the output file has been created.
+  void StartNetLogAfterCreateFile(net::NetLogCaptureMode capture_mode,
+                                  uint64_t max_file_size,
+                                  base::Value custom_constants,
+                                  base::File log_file);
 
-  // Called internally by StopNetLog(). Contains tasks to be done to stop
-  // logging after net-thread polled data is retrieved on the
-  // |net_task_runner_|.
-  void StopNetLogAfterAddNetInfo(
-      std::unique_ptr<base::DictionaryValue> polled_data);
+  void OnStartResult(net::NetLogCaptureMode capture_mode, int result);
+  void OnStopResult(int result);
 
-  // Contains tasks to be done after |file_net_log_observer_| has completely
+  // Contains tasks to be done after |net_log_exporter_| has completely
   // stopped writing.
-  void ResetObserverThenSetStateNotLogging();
+  void ResetExporterThenSetStateNotLogging();
+
+  // Creates a new file for writing at |path|, trying to overwrite any existing
+  // file. Called on |file_task_runner_|.
+  static base::File CreateOutputFile(base::FilePath path);
 
   // All members are accessed solely from the main thread (the thread that
   // |thread_checker_| is bound to).
-
   base::ThreadChecker thread_checker_;
 
   // Task runners for file-specific and net-specific tasks that must run on a
   // file or net task runner.
   scoped_refptr<base::TaskRunner> file_task_runner_;
-  scoped_refptr<base::TaskRunner> net_task_runner_;
 
   State state_;  // Current logging state of NetExportFileWriter.
 
@@ -222,13 +213,8 @@
 
   base::FilePath log_path_;  // base::FilePath to the NetLog file.
 
-  // |file_net_log_observer_| watches the NetLog event stream, and
-  // sends all entries to the file created in StartNetLog().
-  std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_;
-
-  // The |chrome_net_log_| is owned by the browser process, cached here to avoid
-  // using global (g_browser_process).
-  ChromeNetLog* chrome_net_log_;
+  // Used to ask the network service to do the actual exporting.
+  network::mojom::NetLogExporterPtr net_log_exporter_;
 
   // List of StateObservers to notify on state changes.
   base::ObserverList<StateObserver, true> state_observer_list_;
diff --git a/components/net_log/net_export_file_writer_unittest.cc b/components/net_log/net_export_file_writer_unittest.cc
index ebc5cf5..2dfcae0 100644
--- a/components/net_log/net_export_file_writer_unittest.cc
+++ b/components/net_log/net_export_file_writer_unittest.cc
@@ -16,19 +16,22 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/test/scoped_task_environment.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "components/net_log/chrome_net_log.h"
 #include "net/base/test_completion_callback.h"
-#include "net/http/http_network_session.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_event_type.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/network_context.h"
+#include "services/network/network_service.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -162,44 +165,6 @@
   return ::testing::AssertionSuccess();
 }
 
-void SetUpTestContextGetterWithQuicTimeoutInfo(
-    net::NetLog* net_log,
-    int quic_idle_connection_timeout_seconds,
-    scoped_refptr<net::TestURLRequestContextGetter>* context_getter) {
-  std::unique_ptr<net::TestURLRequestContext> context =
-      std::make_unique<net::TestURLRequestContext>(true);
-  context->set_net_log(net_log);
-
-  std::unique_ptr<net::HttpNetworkSession::Params> params(
-      new net::HttpNetworkSession::Params);
-  params->quic_idle_connection_timeout_seconds =
-      quic_idle_connection_timeout_seconds;
-
-  context->set_http_network_session_params(std::move(params));
-  context->Init();
-
-  *context_getter = new net::TestURLRequestContextGetter(
-      base::ThreadTaskRunnerHandle::Get(), std::move(context));
-}
-
-void SetUpTestContextGetterWithRequest(
-    net::NetLog* net_log,
-    const GURL& url,
-    net::URLRequest::Delegate* delegate,
-    scoped_refptr<net::TestURLRequestContextGetter>* context_getter,
-    std::unique_ptr<net::URLRequest>* request) {
-  auto context = std::make_unique<net::TestURLRequestContext>(true);
-  context->set_net_log(net_log);
-  context->Init();
-
-  *request = context->CreateRequest(url, net::IDLE, delegate,
-                                    TRAFFIC_ANNOTATION_FOR_TESTS);
-  (*request)->Start();
-
-  *context_getter = new net::TestURLRequestContextGetter(
-      base::ThreadTaskRunnerHandle::Get(), std::move(context));
-}
-
 // An implementation of NetExportFileWriter::StateObserver that allows waiting
 // until it's notified of a new state.
 class TestStateObserver : public NetExportFileWriter::StateObserver {
@@ -251,15 +216,23 @@
 
 class NetExportFileWriterTest : public ::testing::Test {
  public:
-  using URLRequestContextGetterList =
-      std::vector<scoped_refptr<net::URLRequestContextGetter>>;
-
   NetExportFileWriterTest()
-      : file_writer_(&net_log_), net_thread_("NetLogFileWriter net thread") {}
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO),
+        network_service_(network::NetworkService::CreateForTesting()) {}
 
   // ::testing::Test implementation
   void SetUp() override {
     ASSERT_TRUE(log_temp_dir_.CreateUniqueTempDir());
+    network::mojom::NetworkContextParamsPtr params =
+        network::mojom::NetworkContextParams::New();
+    // Use a fixed proxy config, to avoid dependencies on local network
+    // configuration.
+    params->initial_proxy_config =
+        net::ProxyConfigWithAnnotation::CreateDirect();
+    network_context_ = std::make_unique<network::NetworkContext>(
+        network_service_.get(), mojo::MakeRequest(&network_context_ptr_),
+        std::move(params));
 
     // Override |file_writer_|'s default-log-base-directory-getter to
     // a getter that returns the temp dir created for the test.
@@ -268,8 +241,6 @@
 
     default_log_path_ = log_temp_dir_.GetPath().Append(kLogRelativePath);
 
-    ASSERT_TRUE(net_thread_.Start());
-
     file_writer_.AddObserver(&test_state_observer_);
 
     ASSERT_TRUE(VerifyState(file_writer_.GetState(), kStateUninitializedString,
@@ -291,7 +262,7 @@
   WARN_UNUSED_RESULT ::testing::AssertionResult InitializeThenVerifyNewState(
       bool expected_initialize_success,
       bool expected_log_exists) {
-    file_writer_.Initialize(net_thread_.task_runner());
+    file_writer_.Initialize();
     std::unique_ptr<base::DictionaryValue> state =
         test_state_observer_.WaitForNewState();
     ::testing::AssertionResult result =
@@ -325,10 +296,10 @@
       const base::FilePath& custom_log_path,
       net::NetLogCaptureMode capture_mode,
       const std::string& expected_capture_mode_string,
-      const URLRequestContextGetterList& context_getters) {
+      network::mojom::NetworkContext* network_context) {
     file_writer_.StartNetLog(custom_log_path, capture_mode, kMaxLogSizeBytes,
                              base::CommandLine::StringType(), kChannelString,
-                             context_getters);
+                             network_context);
     std::unique_ptr<base::DictionaryValue> state =
         test_state_observer_.WaitForNewState();
     ::testing::AssertionResult result =
@@ -366,9 +337,8 @@
   WARN_UNUSED_RESULT ::testing::AssertionResult StopThenVerifyNewStateAndFile(
       const base::FilePath& custom_log_path,
       std::unique_ptr<base::DictionaryValue> polled_data,
-      scoped_refptr<net::URLRequestContextGetter> context_getter,
       const std::string& expected_capture_mode_string) {
-    file_writer_.StopNetLog(std::move(polled_data), context_getter);
+    file_writer_.StopNetLog(std::move(polled_data));
     std::unique_ptr<base::DictionaryValue> state =
         test_state_observer_.WaitForNewState();
     ::testing::AssertionResult result =
@@ -415,7 +385,7 @@
     return ::testing::AssertionSuccess();
   }
 
-  ChromeNetLog* net_log() { return &net_log_; }
+  net::NetLog* net_log() { return network_service_->net_log(); }
 
   NetExportFileWriter* file_writer() { return &file_writer_; }
 
@@ -425,17 +395,22 @@
 
   const base::FilePath& default_log_path() const { return default_log_path_; }
 
-  base::Thread* net_thread() { return &net_thread_; }
-
   TestStateObserver* test_state_observer() { return &test_state_observer_; }
 
+  network::mojom::NetworkContext* network_context() {
+    return network_context_ptr_.get();
+  }
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<network::NetworkService> network_service_;
 
-  ChromeNetLog net_log_;
+  network::mojom::NetworkContextPtr network_context_ptr_;
+  std::unique_ptr<network::NetworkContext> network_context_;
 
-  // |file_writer_| is initialized after |net_log_| so that it can stop
-  // obvserving on destruction.
+  // |file_writer_| is initialized after |network_context_ptr_| since
+  // |file_writer_| destructor can talk to mojo objects owned by
+  // |network_context_|.
   NetExportFileWriter file_writer_;
 
   base::ScopedTempDir log_temp_dir_;
@@ -443,8 +418,6 @@
   // The default log path that |file_writer_| will use is cached here.
   base::FilePath default_log_path_;
 
-  base::Thread net_thread_;
-
   TestStateObserver test_state_observer_;
 };
 
@@ -501,41 +474,38 @@
     // StartNetLog(), should result in state change.
     ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[i],
                                         capture_mode_strings[i],
-                                        URLRequestContextGetterList()));
+                                        network_context()));
 
     // Calling StartNetLog() again should be a no-op. Try doing StartNetLog()
     // with various capture modes; they should all be ignored and result in no
     // state change.
-    file_writer()->StartNetLog(base::FilePath(), capture_modes[i],
-                               kMaxLogSizeBytes,
-                               base::CommandLine::StringType(), kChannelString,
-                               URLRequestContextGetterList());
-    file_writer()->StartNetLog(base::FilePath(), capture_modes[(i + 1) % 3],
-                               kMaxLogSizeBytes,
-                               base::CommandLine::StringType(), kChannelString,
-                               URLRequestContextGetterList());
-    file_writer()->StartNetLog(base::FilePath(), capture_modes[(i + 2) % 3],
-                               kMaxLogSizeBytes,
-                               base::CommandLine::StringType(), kChannelString,
-                               URLRequestContextGetterList());
+    file_writer()->StartNetLog(
+        base::FilePath(), capture_modes[i], kMaxLogSizeBytes,
+        base::CommandLine::StringType(), kChannelString, network_context());
+    file_writer()->StartNetLog(
+        base::FilePath(), capture_modes[(i + 1) % 3], kMaxLogSizeBytes,
+        base::CommandLine::StringType(), kChannelString, network_context());
+    file_writer()->StartNetLog(
+        base::FilePath(), capture_modes[(i + 2) % 3], kMaxLogSizeBytes,
+        base::CommandLine::StringType(), kChannelString, network_context());
 
     // StopNetLog(), should result in state change. The capture mode should
     // match that of the first StartNetLog() call (called by
     // StartThenVerifyNewState()).
-    ASSERT_TRUE(StopThenVerifyNewStateAndFile(
-        base::FilePath(), nullptr, nullptr, capture_mode_strings[i]));
+    ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
+                                              capture_mode_strings[i]));
 
     // Stopping a second time should be a no-op.
-    file_writer()->StopNetLog(nullptr, nullptr);
+    file_writer()->StopNetLog(nullptr);
   }
 
   // Start and stop one more time just to make sure the last StopNetLog() call
   // was properly ignored and left |file_writer_| in a valid state.
   ASSERT_TRUE(StartThenVerifyNewState(base::FilePath(), capture_modes[0],
                                       capture_mode_strings[0],
-                                      URLRequestContextGetterList()));
+                                      network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             capture_mode_strings[0]));
 }
 
@@ -546,9 +516,9 @@
 
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             kCaptureModeDefaultString));
 
   int64_t stop_file_size;
@@ -567,9 +537,9 @@
   // before adding the junk data.
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             kCaptureModeDefaultString));
 
   int64_t new_stop_file_size;
@@ -585,9 +555,9 @@
 
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             kCaptureModeDefaultString));
 
   // Get file size without the event.
@@ -596,11 +566,11 @@
 
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
   net_log()->AddGlobalEntry(net::NetLogEventType::CANCELLED);
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             kCaptureModeDefaultString));
 
   // Get file size after adding the event and make sure it's larger than before.
@@ -623,9 +593,9 @@
 
   ASSERT_TRUE(StartThenVerifyNewState(
       custom_log_path, net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr,
                                             kCaptureModeDefaultString));
 
   // Get file size without the event.
@@ -634,11 +604,11 @@
 
   ASSERT_TRUE(StartThenVerifyNewState(
       custom_log_path, net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
   net_log()->AddGlobalEntry(net::NetLogEventType::CANCELLED);
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(custom_log_path, nullptr,
                                             kCaptureModeDefaultString));
 
   // Get file size after adding the event and make sure it's larger than before.
@@ -647,7 +617,7 @@
   EXPECT_GE(new_stop_file_size, stop_file_size);
 }
 
-TEST_F(NetExportFileWriterTest, StopWithPolledDataAndContextGetter) {
+TEST_F(NetExportFileWriterTest, StopWithPolledData) {
   ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
 
   // Create dummy polled data
@@ -657,24 +627,13 @@
       std::make_unique<base::DictionaryValue>();
   dummy_polled_data->SetString(kDummyPolledDataPath, kDummyPolledDataString);
 
-  // Create test context getter on |net_thread_| and wait for it to finish.
-  scoped_refptr<net::TestURLRequestContextGetter> context_getter;
-  const int kDummyQuicParam = 75;
-  net::TestClosure init_done;
-  net_thread()->task_runner()->PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&SetUpTestContextGetterWithQuicTimeoutInfo, net_log(),
-                 kDummyQuicParam, &context_getter),
-      init_done.closure());
-  init_done.WaitForResult();
-
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, URLRequestContextGetterList()));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(
-      base::FilePath(), std::move(dummy_polled_data), context_getter,
-      kCaptureModeDefaultString));
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(),
+                                            std::move(dummy_polled_data),
+                                            kCaptureModeDefaultString));
 
   // Read polledData from log file.
   std::unique_ptr<base::DictionaryValue> root;
@@ -687,44 +646,74 @@
   ASSERT_TRUE(polled_data->GetString(kDummyPolledDataPath, &dummy_string));
   EXPECT_EQ(kDummyPolledDataString, dummy_string);
 
-  // Check that it also contains the field from the URLRequestContext that was
-  // passed in.
-  base::DictionaryValue* quic_info;
-  ASSERT_TRUE(polled_data->GetDictionary("quicInfo", &quic_info));
-  base::Value* timeout_value = nullptr;
-  int timeout;
-  ASSERT_TRUE(
-      quic_info->Get("idle_connection_timeout_seconds", &timeout_value));
-  ASSERT_TRUE(timeout_value->GetAsInteger(&timeout));
-  EXPECT_EQ(kDummyQuicParam, timeout);
+  // Check that it also contains something from net::GetNetInfo.
+  base::DictionaryValue* http_cache_info;
+  ASSERT_TRUE(polled_data->GetDictionary("httpCacheInfo", &http_cache_info));
 }
 
-TEST_F(NetExportFileWriterTest, StartWithContextGetters) {
+// Test with requests in flight. This is done by going through a sequence of a
+// redirect --- at which point the log is started --- and then a fetch of
+// destination that's blocked on an event in EmbeddedTestServer.
+TEST_F(NetExportFileWriterTest, StartWithNetworkContextActive) {
+  net::EmbeddedTestServer test_server;
+  net::test_server::RegisterDefaultHandlers(&test_server);
+
+  base::WaitableEvent block_fetch(
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  test_server.RegisterRequestHandler(base::BindRepeating(
+      [](base::WaitableEvent* block_fetch,
+         const net::test_server::HttpRequest& request)
+          -> std::unique_ptr<net::test_server::HttpResponse> {
+        if (request.relative_url == "/block")
+          block_fetch->Wait();
+        return nullptr;
+      },
+      &block_fetch));
+
+  ASSERT_TRUE(test_server.Start());
+
   ASSERT_TRUE(InitializeThenVerifyNewState(true, false));
 
-  // Create test context getter and request on |net_thread_| and wait for it to
-  // finish.
-  const std::string kDummyUrl = "blah:blah";
-  scoped_refptr<net::TestURLRequestContextGetter> context_getter;
-  std::unique_ptr<net::URLRequest> request;
-  net::TestDelegate delegate;
-  delegate.set_quit_on_complete(false);
+  network::mojom::URLLoaderFactoryPtr url_loader_factory;
+  network_context()->CreateURLLoaderFactory(
+      mojo::MakeRequest(&url_loader_factory), 0);
 
-  net::TestClosure init_done;
-  net_thread()->task_runner()->PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&SetUpTestContextGetterWithRequest, net_log(), GURL(kDummyUrl),
-                 &delegate, &context_getter, &request),
-      init_done.closure());
-  init_done.WaitForResult();
+  const char kRedirectURL[] = "/server-redirect?/block";
+  std::unique_ptr<network::ResourceRequest> request =
+      std::make_unique<network::ResourceRequest>();
+  request->url = test_server.GetURL(kRedirectURL);
 
+  std::unique_ptr<network::SimpleURLLoader> simple_loader =
+      network::SimpleURLLoader::Create(std::move(request),
+                                       TRAFFIC_ANNOTATION_FOR_TESTS);
+  base::RunLoop run_loop, run_loop2;
+  simple_loader->SetOnRedirectCallback(base::BindRepeating(
+      [](base::RepeatingClosure notify_log,
+         const net::RedirectInfo& redirect_info,
+         const network::ResourceResponseHead& response_head) {
+        notify_log.Run();
+      },
+      run_loop.QuitClosure()));
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory.get(),
+      base::BindOnce(
+          [](base::OnceClosure quit_closure,
+             std::unique_ptr<std::string> response_body) {
+            std::move(quit_closure).Run();
+          },
+          run_loop2.QuitClosure()));
+
+  // Wait for fetch to get some bytes accross. It will not be the entire
+  // thing since the post-redirect URL will get blocked by the custom handler.
+  run_loop.Run();
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::Default(),
-      kCaptureModeDefaultString, {context_getter}));
+      kCaptureModeDefaultString, network_context()));
 
-  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr, nullptr,
+  ASSERT_TRUE(StopThenVerifyNewStateAndFile(base::FilePath(), nullptr,
                                             kCaptureModeDefaultString));
-
   // Read events from log file.
   std::unique_ptr<base::DictionaryValue> root;
   ASSERT_TRUE(ReadCompleteLogFile(default_log_path(), &root));
@@ -732,7 +721,7 @@
   ASSERT_TRUE(root->GetList("events", &events));
 
   // Check there is at least one event as a result of the ongoing request.
-  EXPECT_GE(events->GetSize(), 1u);
+  ASSERT_GE(events->GetSize(), 1u);
 
   // Check the URL in the params of the first event.
   base::DictionaryValue* event;
@@ -741,24 +730,24 @@
   EXPECT_TRUE(event->GetDictionary("params", &event_params));
   std::string event_url;
   EXPECT_TRUE(event_params->GetString("url", &event_url));
-  EXPECT_EQ(kDummyUrl, event_url);
+  EXPECT_EQ(test_server.GetURL(kRedirectURL), event_url);
 
-  net_thread()->task_runner()->DeleteSoon(FROM_HERE, request.release());
+  block_fetch.Signal();
+  run_loop2.Run();
 }
 
 TEST_F(NetExportFileWriterTest, ReceiveStartWhileInitializing) {
   // Trigger initialization of |file_writer_|.
-  file_writer()->Initialize(net_thread()->task_runner());
+  file_writer()->Initialize();
 
   // Before running the main message loop, tell |file_writer_| to start
   // logging. Not running the main message loop prevents the initialization
   // process from completing, so this ensures that StartNetLog() is received
   // before |file_writer_| finishes initialization, which means this
   // should be a no-op.
-  file_writer()->StartNetLog(base::FilePath(),
-                             net::NetLogCaptureMode::Default(),
-                             kMaxLogSizeBytes, base::CommandLine::StringType(),
-                             kChannelString, URLRequestContextGetterList());
+  file_writer()->StartNetLog(
+      base::FilePath(), net::NetLogCaptureMode::Default(), kMaxLogSizeBytes,
+      base::CommandLine::StringType(), kChannelString, network_context());
 
   // Now run the main message loop. Make sure StartNetLog() was ignored by
   // checking that the next two states are "initializing" followed by
@@ -777,20 +766,19 @@
   // Call StartNetLog() on |file_writer_| and wait for the state change.
   ASSERT_TRUE(StartThenVerifyNewState(
       base::FilePath(), net::NetLogCaptureMode::IncludeSocketBytes(),
-      kCaptureModeIncludeSocketBytesString, URLRequestContextGetterList()));
+      kCaptureModeIncludeSocketBytesString, network_context()));
 
   // Tell |file_writer_| to stop logging.
-  file_writer()->StopNetLog(nullptr, nullptr);
+  file_writer()->StopNetLog(nullptr);
 
   // Before running the main message loop, tell |file_writer_| to start
   // logging. Not running the main message loop prevents the stopping process
   // from completing, so this ensures StartNetLog() is received before
   // |file_writer_| finishes stopping, which means this should be a
   // no-op.
-  file_writer()->StartNetLog(base::FilePath(),
-                             net::NetLogCaptureMode::Default(),
-                             kMaxLogSizeBytes, base::CommandLine::StringType(),
-                             kChannelString, URLRequestContextGetterList());
+  file_writer()->StartNetLog(
+      base::FilePath(), net::NetLogCaptureMode::Default(), kMaxLogSizeBytes,
+      base::CommandLine::StringType(), kChannelString, network_context());
 
   // Now run the main message loop. Make sure the last StartNetLog() was
   // ignored by checking that the next two states are "stopping-log" followed by
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index 0fc12b6b..ce663a2 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -18,6 +18,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
 #include "base/values.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
@@ -295,7 +296,8 @@
         })");
   large_icon_service_
       ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          publisher_url, minimum_size_in_pixel, desired_size_in_pixel,
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              publisher_url, minimum_size_in_pixel, desired_size_in_pixel),
           /*may_page_url_be_private=*/false, traffic_annotation,
           base::Bind(
               &ContentSuggestionsService::OnGetFaviconFromGoogleServerFinished,
diff --git a/components/ntp_snippets_strings_grdp/OWNERS b/components/ntp_snippets_strings_grdp/OWNERS
new file mode 100644
index 0000000..c7f9cc3f
--- /dev/null
+++ b/components/ntp_snippets_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/ntp_snippets/OWNERS
diff --git a/components/ntp_snippets_strings_grdp/README.md b/components/ntp_snippets_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/ntp_snippets_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/ntp_tiles/icon_cacher_impl.cc b/components/ntp_tiles/icon_cacher_impl.cc
index ad8cfdd..2b5853e 100644
--- a/components/ntp_tiles/icon_cacher_impl.cc
+++ b/components/ntp_tiles/icon_cacher_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/favicon_util.h"
 #include "components/favicon/core/large_icon_service.h"
@@ -264,9 +265,10 @@
         })");
   large_icon_service_
       ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-          page_url,
-          GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer(),
-          GetDesiredFetchingSizeForChromeSuggestionsFaviconsFromServer(),
+          favicon::FaviconServerFetcherParams::CreateForMobile(
+              page_url,
+              GetMinimumFetchingSizeForChromeSuggestionsFaviconsFromServer(),
+              GetDesiredFetchingSizeForChromeSuggestionsFaviconsFromServer()),
           /*may_page_url_be_private=*/true, traffic_annotation,
           base::Bind(&IconCacherImpl::OnMostLikelyFaviconDownloaded,
                      weak_ptr_factory_.GetWeakPtr(), page_url));
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn
index b2f88908..e4d2bf8a 100644
--- a/components/offline_pages/core/BUILD.gn
+++ b/components/offline_pages/core/BUILD.gn
@@ -36,6 +36,8 @@
     "model/get_pages_task.h",
     "model/get_thumbnail_task.cc",
     "model/get_thumbnail_task.h",
+    "model/has_thumbnail_task.cc",
+    "model/has_thumbnail_task.h",
     "model/mark_page_accessed_task.cc",
     "model/mark_page_accessed_task.h",
     "model/offline_page_model_taskified.cc",
@@ -166,6 +168,7 @@
     "model/delete_page_task_unittest.cc",
     "model/get_pages_task_unittest.cc",
     "model/get_thumbnail_task_unittest.cc",
+    "model/has_thumbnail_task_unittest.cc",
     "model/mark_page_accessed_task_unittest.cc",
     "model/offline_page_model_taskified_unittest.cc",
     "model/offline_page_model_utils_unittest.cc",
diff --git a/components/offline_pages/core/model/has_thumbnail_task.cc b/components/offline_pages/core/model/has_thumbnail_task.cc
new file mode 100644
index 0000000..695f81e
--- /dev/null
+++ b/components/offline_pages/core/model/has_thumbnail_task.cc
@@ -0,0 +1,47 @@
+// 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 "components/offline_pages/core/model/has_thumbnail_task.h"
+
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace offline_pages {
+
+namespace {
+
+bool ThumbnailExistsSync(int64_t offline_id, sql::Connection* db) {
+  static const char kSql[] =
+      "SELECT 1 FROM page_thumbnails WHERE offline_id = ?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, offline_id);
+  return statement.Step();
+}
+
+}  // namespace
+
+HasThumbnailTask::HasThumbnailTask(OfflinePageMetadataStoreSQL* store,
+                                   int64_t offline_id,
+                                   ThumbnailExistsCallback exists_callback)
+    : store_(store),
+      offline_id_(offline_id),
+      exists_callback_(std::move(exists_callback)),
+      weak_ptr_factory_(this) {}
+
+HasThumbnailTask::~HasThumbnailTask() = default;
+
+void HasThumbnailTask::Run() {
+  store_->Execute(base::BindOnce(ThumbnailExistsSync, std::move(offline_id_)),
+                  base::BindOnce(&HasThumbnailTask::OnThumbnailExists,
+                                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void HasThumbnailTask::OnThumbnailExists(bool exists) {
+  TaskComplete();
+  std::move(exists_callback_).Run(exists);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/model/has_thumbnail_task.h b/components/offline_pages/core/model/has_thumbnail_task.h
new file mode 100644
index 0000000..80b53d8cc
--- /dev/null
+++ b/components/offline_pages/core/model/has_thumbnail_task.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 COMPONENTS_OFFLINE_PAGES_CORE_MODEL_HAS_THUMBNAIL_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_MODEL_HAS_THUMBNAIL_TASK_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+class OfflinePageMetadataStoreSQL;
+
+// Checks if a thumbnail exists for the specified offline id.
+class HasThumbnailTask : public Task {
+ public:
+  using ThumbnailExistsCallback = base::OnceCallback<void(bool)>;
+
+  HasThumbnailTask(OfflinePageMetadataStoreSQL* store,
+                   int64_t offline_id,
+                   ThumbnailExistsCallback exists_callback);
+  ~HasThumbnailTask() override;
+
+  // Task implementation:
+  void Run() override;
+
+ private:
+  void OnThumbnailExists(bool exists);
+
+  OfflinePageMetadataStoreSQL* store_;
+  int64_t offline_id_;
+  ThumbnailExistsCallback exists_callback_;
+  base::WeakPtrFactory<HasThumbnailTask> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(HasThumbnailTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_MODEL_HAS_THUMBNAIL_TASK_H_
diff --git a/components/offline_pages/core/model/has_thumbnail_task_unittest.cc b/components/offline_pages/core/model/has_thumbnail_task_unittest.cc
new file mode 100644
index 0000000..147330e
--- /dev/null
+++ b/components/offline_pages/core/model/has_thumbnail_task_unittest.cc
@@ -0,0 +1,46 @@
+// 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 "components/offline_pages/core/model/has_thumbnail_task.h"
+
+#include <memory>
+
+#include "base/bind_helpers.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
+#include "components/offline_pages/core/model/model_task_test_base.h"
+#include "components/offline_pages/core/model/store_thumbnail_task.h"
+#include "components/offline_pages/core/offline_store_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+
+using ThumbnailExistsCallback = HasThumbnailTask::ThumbnailExistsCallback;
+
+class HasThumbnailTaskTest : public ModelTaskTestBase {};
+
+TEST_F(HasThumbnailTaskTest, CorrectlyFindsById) {
+  const int64_t valid_offline_id = 1;
+  const int64_t invalid_offline_id = 2;
+  OfflinePageThumbnail thumb;
+  thumb.offline_id = valid_offline_id;
+  thumb.expiration = store_utils::FromDatabaseTime(1234);
+  thumb.thumbnail = "123abc";
+  RunTask(
+      std::make_unique<StoreThumbnailTask>(store(), thumb, base::DoNothing()));
+
+  base::MockCallback<ThumbnailExistsCallback> exists_callback;
+  EXPECT_CALL(exists_callback, Run(true));
+  RunTask(std::make_unique<HasThumbnailTask>(store(), valid_offline_id,
+                                             exists_callback.Get()));
+
+  base::MockCallback<ThumbnailExistsCallback> doesnt_exist_callback;
+  EXPECT_CALL(doesnt_exist_callback, Run(false));
+  RunTask(std::make_unique<HasThumbnailTask>(store(), invalid_offline_id,
+                                             doesnt_exist_callback.Get()));
+}
+
+}  // namespace
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/model/offline_page_model_taskified.cc b/components/offline_pages/core/model/offline_page_model_taskified.cc
index a9a580f..afc579cc 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified.cc
@@ -26,6 +26,7 @@
 #include "components/offline_pages/core/model/delete_page_task.h"
 #include "components/offline_pages/core/model/get_pages_task.h"
 #include "components/offline_pages/core/model/get_thumbnail_task.h"
+#include "components/offline_pages/core/model/has_thumbnail_task.h"
 #include "components/offline_pages/core/model/mark_page_accessed_task.h"
 #include "components/offline_pages/core/model/offline_page_model_utils.h"
 #include "components/offline_pages/core/model/persistent_page_consistency_check_task.h"
@@ -426,6 +427,13 @@
       store_.get(), offline_id, std::move(callback)));
 }
 
+void OfflinePageModelTaskified::HasThumbnailForOfflineId(
+    int64_t offline_id,
+    base::OnceCallback<void(bool)> callback) {
+  task_queue_.AddTask(std::make_unique<HasThumbnailTask>(
+      store_.get(), offline_id, std::move(callback)));
+}
+
 const base::FilePath& OfflinePageModelTaskified::GetInternalArchiveDirectory(
     const std::string& name_space) const {
   if (policy_controller_->IsRemovedOnCacheReset(name_space))
diff --git a/components/offline_pages/core/model/offline_page_model_taskified.h b/components/offline_pages/core/model/offline_page_model_taskified.h
index 52215f2a1..adfb81d 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified.h
+++ b/components/offline_pages/core/model/offline_page_model_taskified.h
@@ -135,6 +135,9 @@
       int64_t offline_id,
       base::OnceCallback<void(std::unique_ptr<OfflinePageThumbnail>)> callback)
       override;
+  void HasThumbnailForOfflineId(
+      int64_t offline_id,
+      base::OnceCallback<void(bool)> callback) override;
 
   const base::FilePath& GetInternalArchiveDirectory(
       const std::string& name_space) const override;
diff --git a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
index 714f403b..0a7d610 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
@@ -162,20 +162,6 @@
     return model_->last_maintenance_tasks_schedule_time_;
   }
 
-  std::unique_ptr<OfflinePageThumbnail> GetThumbnailSync(int64_t offline_id) {
-    bool called = false;
-    std::unique_ptr<OfflinePageThumbnail> result;
-    auto callback = base::BindLambdaForTesting(
-        [&](std::unique_ptr<OfflinePageThumbnail> thumbnail) {
-          called = true;
-          result = std::move(thumbnail);
-        });
-    model_->GetThumbnailByOfflineId(offline_id, callback);
-    PumpLoop();
-    EXPECT_TRUE(called);
-    return result;
-  }
-
  private:
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -1666,7 +1652,8 @@
                                        0);
 }
 
-TEST_F(OfflinePageModelTaskifiedTest, StoreAndGetThumbnail) {
+TEST_F(OfflinePageModelTaskifiedTest, StoreAndCheckThumbnail) {
+  // Store a thumbnail.
   OfflinePageThumbnail thumb;
   thumb.offline_id = 1;
   thumb.expiration = base::Time::Now();
@@ -1675,12 +1662,21 @@
   EXPECT_CALL(*this, ThumbnailAdded(_, thumb));
   PumpLoop();
 
+  // Check it exists
+  bool thumbnail_exists = false;
+  auto exists_callback = base::BindLambdaForTesting(
+      [&](bool exists) { thumbnail_exists = exists; });
+  model()->HasThumbnailForOfflineId(thumb.offline_id, exists_callback);
+  PumpLoop();
+  EXPECT_TRUE(thumbnail_exists);
+
+  // Obtain its data.
   std::unique_ptr<OfflinePageThumbnail> result_thumbnail;
-  auto callback = base::BindLambdaForTesting(
+  auto data_callback = base::BindLambdaForTesting(
       [&](std::unique_ptr<OfflinePageThumbnail> result) {
         result_thumbnail = std::move(result);
       });
-  model()->GetThumbnailByOfflineId(thumb.offline_id, callback);
+  model()->GetThumbnailByOfflineId(thumb.offline_id, data_callback);
   PumpLoop();
   EXPECT_EQ(thumb, *result_thumbnail);
 }
diff --git a/components/offline_pages/core/offline_page_model.h b/components/offline_pages/core/offline_page_model.h
index 141e59c..599de5b 100644
--- a/components/offline_pages/core/offline_page_model.h
+++ b/components/offline_pages/core/offline_page_model.h
@@ -231,6 +231,12 @@
   virtual void GetThumbnailByOfflineId(int64_t offline_id,
                                        GetThumbnailCallback callback) = 0;
 
+  // Checks if a thumbnail for a specific |offline_id| exists in the
+  // page_thumbnails table. Calls callback with the bool result.
+  virtual void HasThumbnailForOfflineId(
+      int64_t offline_id,
+      base::OnceCallback<void(bool)> callback) = 0;
+
   // Publishes an offline page from the internal offline page directory.  This
   // includes putting it in a public directory, updating the system download
   // manager, if any, and updating the offline page model database.
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_task.cc b/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
index 345d38bf..adc45fb 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
@@ -8,7 +8,9 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/time/default_clock.h"
+#include "components/offline_pages/core/client_id.h"
 #include "components/offline_pages/core/offline_store_utils.h"
 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
@@ -18,19 +20,32 @@
 #include "sql/transaction.h"
 
 namespace offline_pages {
+
+// A wrapper for two vectors, one with string URLs and one with offline and
+// client ids pairs, each holding data for the same set of prefetch items.
+struct GeneratePageBundleTask::UrlAndIds {
+  std::vector<std::string> urls;
+  PrefetchDispatcher::IdsVector ids;
+};
+
 namespace {
 
+using UrlAndIds = GeneratePageBundleTask::UrlAndIds;
+
 // Temporary storage for Urls metadata fetched from the storage.
 struct FetchedUrl {
   FetchedUrl() = default;
   FetchedUrl(int64_t offline_id,
+             ClientId client_id,
              const std::string& requested_url,
              int generate_bundle_attempts)
       : offline_id(offline_id),
+        client_id(client_id),
         requested_url(requested_url),
         generate_bundle_attempts(generate_bundle_attempts) {}
 
   int64_t offline_id;
+  ClientId client_id;
   std::string requested_url;
   int generate_bundle_attempts;
 };
@@ -58,7 +73,8 @@
 
 std::unique_ptr<std::vector<FetchedUrl>> FetchUrlsSync(sql::Connection* db) {
   static const char kSql[] =
-      "SELECT offline_id, requested_url, generate_bundle_attempts"
+      "SELECT offline_id, client_namespace, client_id, requested_url,"
+      "       generate_bundle_attempts"
       " FROM prefetch_items"
       " WHERE state = ?"
       " ORDER BY creation_time DESC";
@@ -67,10 +83,15 @@
 
   auto urls = std::make_unique<std::vector<FetchedUrl>>();
   while (statement.Step()) {
-    urls->push_back(
-        FetchedUrl(statement.ColumnInt64(0),   // offline_id
-                   statement.ColumnString(1),  // requested_url
-                   statement.ColumnInt(2)));   // generate_bundle_attempts
+    urls->push_back(FetchedUrl(
+        // offline_id
+        statement.ColumnInt64(0),
+        // client_id
+        {statement.ColumnString(1), statement.ColumnString(2)},
+        // requested_url
+        statement.ColumnString(3),
+        // generate_bundle_attempts
+        statement.ColumnInt(4)));
   }
 
   return urls;
@@ -88,9 +109,8 @@
   return statement.Run();
 }
 
-std::unique_ptr<std::vector<std::string>> SelectUrlsToPrefetchSync(
-    base::Clock* clock,
-    sql::Connection* db) {
+std::unique_ptr<UrlAndIds> SelectUrlsToPrefetchSync(base::Clock* clock,
+                                                    sql::Connection* db) {
   if (!db)
     return nullptr;
 
@@ -112,26 +132,29 @@
     urls->resize(kMaxUrlsToSend);
   }
 
-  auto url_specs = std::make_unique<std::vector<std::string>>();
+  auto url_and_ids = std::make_unique<UrlAndIds>();
   for (const auto& url : *urls) {
     if (!UpdateStateSync(db, url.offline_id, clock))
       return nullptr;
-    url_specs->push_back(std::move(url.requested_url));
+    url_and_ids->urls.push_back(std::move(url.requested_url));
+    url_and_ids->ids.push_back({url.offline_id, std::move(url.client_id)});
   }
 
   if (!transaction.Commit())
     return nullptr;
 
-  return url_specs;
+  return url_and_ids;
 }
 }  // namespace
 
 GeneratePageBundleTask::GeneratePageBundleTask(
+    PrefetchDispatcher* prefetch_dispatcher,
     PrefetchStore* prefetch_store,
     PrefetchGCMHandler* gcm_handler,
     PrefetchNetworkRequestFactory* request_factory,
     const PrefetchRequestFinishedCallback& callback)
     : clock_(base::DefaultClock::GetInstance()),
+      prefetch_dispatcher_(prefetch_dispatcher),
       prefetch_store_(prefetch_store),
       gcm_handler_(gcm_handler),
       request_factory_(request_factory),
@@ -152,23 +175,30 @@
 }
 
 void GeneratePageBundleTask::StartGeneratePageBundle(
-    std::unique_ptr<std::vector<std::string>> urls) {
-  if (!urls || urls->empty()) {
+    std::unique_ptr<UrlAndIds> url_and_ids) {
+  if (!url_and_ids) {
     TaskComplete();
     return;
   }
+  DCHECK(!url_and_ids->urls.empty());
+  DCHECK_EQ(url_and_ids->urls.size(), url_and_ids->ids.size());
 
-  gcm_handler_->GetGCMToken(
-      base::Bind(&GeneratePageBundleTask::GotRegistrationId,
-                 weak_factory_.GetWeakPtr(), base::Passed(std::move(urls))));
+  gcm_handler_->GetGCMToken(base::AdaptCallbackForRepeating(base::BindOnce(
+      &GeneratePageBundleTask::GotRegistrationId, weak_factory_.GetWeakPtr(),
+      base::Passed(std::move(url_and_ids)))));
 }
 
 void GeneratePageBundleTask::GotRegistrationId(
-    std::unique_ptr<std::vector<std::string>> urls,
+    std::unique_ptr<UrlAndIds> url_and_ids,
     const std::string& id,
     instance_id::InstanceID::Result result) {
+  DCHECK(url_and_ids);
   // TODO(dimich): Add UMA reporting on instance_id::InstanceID::Result.
-  request_factory_->MakeGeneratePageBundleRequest(*urls, id, callback_);
+  request_factory_->MakeGeneratePageBundleRequest(url_and_ids->urls, id,
+                                                  callback_);
+  prefetch_dispatcher_->GeneratePageBundleRequested(
+      std::make_unique<PrefetchDispatcher::IdsVector>(
+          std::move(url_and_ids->ids)));
   TaskComplete();
 }
 
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_task.h b/components/offline_pages/core/prefetch/generate_page_bundle_task.h
index 7228eec..0430164 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_task.h
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_task.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/clock.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "components/offline_pages/core/task.h"
 
@@ -24,7 +25,10 @@
 // determined are viable to prefetch.
 class GeneratePageBundleTask : public Task {
  public:
-  GeneratePageBundleTask(PrefetchStore* prefetch_store,
+  struct UrlAndIds;
+
+  GeneratePageBundleTask(PrefetchDispatcher* prefetch_dispatcher,
+                         PrefetchStore* prefetch_store,
                          PrefetchGCMHandler* gcm_handler,
                          PrefetchNetworkRequestFactory* request_factory,
                          const PrefetchRequestFinishedCallback& callback);
@@ -36,13 +40,14 @@
   void SetClockForTesting(base::Clock* clock);
 
  private:
-  void StartGeneratePageBundle(std::unique_ptr<std::vector<std::string>> urls);
-  void GotRegistrationId(std::unique_ptr<std::vector<std::string>> urls,
+  void StartGeneratePageBundle(std::unique_ptr<UrlAndIds> url_and_ids);
+  void GotRegistrationId(std::unique_ptr<UrlAndIds> url_and_ids,
                          const std::string& id,
                          instance_id::InstanceID::Result result);
 
   base::Clock* clock_;
 
+  PrefetchDispatcher* prefetch_dispatcher_;
   PrefetchStore* prefetch_store_;
   PrefetchGCMHandler* gcm_handler_;
   PrefetchNetworkRequestFactory* request_factory_;
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc b/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc
index e185d99..0394acb 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_task_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/offline_pages/core/prefetch/generate_page_bundle_task.h"
 
+#include <utility>
+
 #include "base/logging.h"
 #include "base/test/mock_callback.h"
 #include "base/test/simple_test_clock.h"
@@ -14,6 +16,7 @@
 #include "components/offline_pages/core/prefetch/store/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_store_utils.h"
+#include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
 #include "components/offline_pages/core/prefetch/test_prefetch_gcm_handler.h"
 #include "components/offline_pages/core/task.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -35,8 +38,11 @@
 
   TestPrefetchGCMHandler* gcm_handler() { return &gcm_handler_; }
 
+  TestPrefetchDispatcher* dispatcher() { return &dispatcher_; }
+
  private:
   TestPrefetchGCMHandler gcm_handler_;
+  TestPrefetchDispatcher dispatcher_;
 };
 
 TEST_F(GeneratePageBundleTaskTest, StoreFailure) {
@@ -44,17 +50,21 @@
 
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   RunTask(std::make_unique<GeneratePageBundleTask>(
-      store(), gcm_handler(), prefetch_request_factory(), callback.Get()));
+      dispatcher(), store(), gcm_handler(), prefetch_request_factory(),
+      callback.Get()));
+  EXPECT_EQ(0, dispatcher()->generate_page_bundle_requested);
 }
 
 TEST_F(GeneratePageBundleTaskTest, EmptyTask) {
   base::MockCallback<PrefetchRequestFinishedCallback> callback;
   RunTask(std::make_unique<GeneratePageBundleTask>(
-      store(), gcm_handler(), prefetch_request_factory(), callback.Get()));
+      dispatcher(), store(), gcm_handler(), prefetch_request_factory(),
+      callback.Get()));
 
   EXPECT_FALSE(prefetch_request_factory()->HasOutstandingRequests());
   auto requested_urls = prefetch_request_factory()->GetAllUrlsRequested();
   EXPECT_TRUE(requested_urls->empty());
+  EXPECT_EQ(0, dispatcher()->generate_page_bundle_requested);
 }
 
 TEST_F(GeneratePageBundleTaskTest, TaskMakesNetworkRequest) {
@@ -62,6 +72,7 @@
 
   base::SimpleTestClock clock;
 
+  // This item will be sent with the bundle request.
   PrefetchItem item1 =
       item_generator()->CreateItem(PrefetchItemState::NEW_REQUEST);
   item1.freshness_time = clock.Now();
@@ -70,6 +81,8 @@
 
   clock.Advance(base::TimeDelta::FromSeconds(1));
 
+  // This item will also be sent with the bundle request but being the freshest
+  // it will come first in the list.
   PrefetchItem item2 =
       item_generator()->CreateItem(PrefetchItemState::NEW_REQUEST);
   item1.freshness_time = clock.Now();
@@ -82,18 +95,33 @@
   PrefetchItem item3 =
       item_generator()->CreateItem(PrefetchItemState::FINISHED);
   EXPECT_TRUE(store_util()->InsertPrefetchItem(item3));
+  EXPECT_NE(item3.offline_id, item1.offline_id);
+  EXPECT_NE(item3.offline_id, item2.offline_id);
 
   EXPECT_EQ(3, store_util()->CountPrefetchItems());
 
   clock.Advance(base::TimeDelta::FromHours(1));
 
-  GeneratePageBundleTask task(store(), gcm_handler(),
+  GeneratePageBundleTask task(dispatcher(), store(), gcm_handler(),
                               prefetch_request_factory(),
                               request_callback.Get());
   task.SetClockForTesting(&clock);
   RunTask(&task);
 
-  auto requested_urls = prefetch_request_factory()->GetAllUrlsRequested();
+  // Note: even though the requested URLs checked further below are in undefined
+  // order (due to use of std::set) their order of requesting is known: latest
+  // creation dates should come first. But as these ids are stored in a
+  // std::vector we can rely on the order being correct.
+  EXPECT_EQ(1, dispatcher()->generate_page_bundle_requested);
+  EXPECT_EQ(2u, dispatcher()->ids_from_generate_page_bundle_requested->size());
+  EXPECT_EQ(std::make_pair(item1.offline_id, item1.client_id),
+            dispatcher()->ids_from_generate_page_bundle_requested->at(1));
+  EXPECT_EQ(std::make_pair(item2.offline_id, item2.client_id),
+            dispatcher()->ids_from_generate_page_bundle_requested->at(0));
+
+  std::unique_ptr<std::set<std::string>> requested_urls =
+      prefetch_request_factory()->GetAllUrlsRequested();
+  EXPECT_EQ(2u, requested_urls->size());
   EXPECT_THAT(*requested_urls, Contains(item1.url.spec()));
   EXPECT_THAT(*requested_urls, Contains(item2.url.spec()));
   EXPECT_THAT(*requested_urls, Not(Contains(item3.url.spec())));
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher.h b/components/offline_pages/core/prefetch/prefetch_dispatcher.h
index 0cfc6cf..f1e551d 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher.h
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher.h
@@ -44,6 +44,9 @@
 //    Reconcilers during BackgroundTask processing.
 class PrefetchDispatcher {
  public:
+  // Vector of pairs of offline and client IDs.
+  using IdsVector = std::vector<std::pair<int64_t, ClientId>>;
+
   virtual ~PrefetchDispatcher() = default;
 
   // Initializes the dispatcher with its respective service instance. This must
@@ -104,6 +107,9 @@
       const std::map<std::string, std::pair<base::FilePath, int64_t>>&
           success_downloads) = 0;
 
+  // Called when a GeneratePageBundle request has been sent.
+  virtual void GeneratePageBundleRequested(std::unique_ptr<IdsVector> ids) = 0;
+
   // Called when a download is completed successfully or fails.
   virtual void DownloadCompleted(
       const PrefetchDownloadResult& download_result) = 0;
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
index c1b0441c..69ae1afd 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -49,26 +50,11 @@
 namespace offline_pages {
 
 namespace {
+
 void DeleteBackgroundTaskHelper(std::unique_ptr<PrefetchBackgroundTask> task) {
   task.reset();
 }
 
-void FetchComplete(OfflinePageModel* offline_model,
-                   int64_t offline_id,
-                   const std::string& image_data) {
-  if (image_data.empty())
-    return;
-  // Thumbnails are marked to expire after this delta. Expired thumbnails are
-  // eventually deleted if their offline_id does not correspond to an offline
-  // item. Two days gives us plenty of time so that the prefetched item can be
-  // imported into the offline item database.
-  const base::TimeDelta kThumbnailExpirationDelta =
-      base::TimeDelta::FromDays(2);
-
-  offline_model->StoreThumbnail(OfflinePageThumbnail(
-      offline_id, base::Time::Now() + kThumbnailExpirationDelta, image_data));
-}
-
 }  // namespace
 
 PrefetchDispatcherImpl::PrefetchDispatcherImpl()
@@ -230,7 +216,7 @@
 
   std::unique_ptr<Task> generate_page_bundle_task =
       std::make_unique<GeneratePageBundleTask>(
-          service_->GetPrefetchStore(), service_->GetPrefetchGCMHandler(),
+          this, service_->GetPrefetchStore(), service_->GetPrefetchGCMHandler(),
           service_->GetPrefetchNetworkRequestFactory(),
           base::Bind(
               &PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest,
@@ -332,6 +318,14 @@
       success_downloads));
 }
 
+void PrefetchDispatcherImpl::GeneratePageBundleRequested(
+    std::unique_ptr<PrefetchDispatcher::IdsVector> ids) {
+  // Reverse the order so that the fresher items are last. This is done because
+  // the ids are popped from the end of the vector.
+  std::reverse(ids->begin(), ids->end());
+  FetchThumbnails(std::move(ids));
+}
+
 void PrefetchDispatcherImpl::DownloadCompleted(
     const PrefetchDownloadResult& download_result) {
   if (!service_->GetPrefetchConfiguration()->IsPrefetchingEnabled())
@@ -353,12 +347,9 @@
 
 void PrefetchDispatcherImpl::ItemDownloaded(int64_t offline_id,
                                             const ClientId& client_id) {
-  DCHECK(client_id.name_space == kSuggestedArticlesNamespace);
-  auto complete_callback = base::BindOnce(
-      &FetchComplete, base::Unretained(service_->GetOfflinePageModel()),
-      offline_id);
-  service_->GetThumbnailFetcher()->FetchSuggestionImageData(
-      client_id, std::move(complete_callback));
+  auto ids = std::make_unique<IdsVector>();
+  ids->emplace_back(offline_id, client_id);
+  FetchThumbnails(std::move(ids));
 }
 
 void PrefetchDispatcherImpl::ArchiveImported(int64_t offline_id, bool success) {
@@ -396,4 +387,55 @@
   }
 }
 
+void PrefetchDispatcherImpl::FetchThumbnails(
+    std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids) {
+  if (remaining_ids->empty())
+    return;
+
+  int64_t offline_id = remaining_ids->back().first;
+  ClientId client_id = std::move(remaining_ids->back().second);
+  DCHECK(client_id.name_space == kSuggestedArticlesNamespace);
+  remaining_ids->pop_back();
+
+  service_->GetOfflinePageModel()->HasThumbnailForOfflineId(
+      offline_id,
+      base::BindOnce(&PrefetchDispatcherImpl::ThumbnailExistenceChecked,
+                     base::Unretained(this), offline_id, std::move(client_id),
+                     std::move(remaining_ids)));
+}
+
+void PrefetchDispatcherImpl::ThumbnailExistenceChecked(
+    const int64_t offline_id,
+    ClientId client_id,
+    std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,
+    bool thumbnail_exists) {
+  if (thumbnail_exists) {
+    ThumbnailFetchComplete(offline_id, std::move(remaining_ids), std::string());
+  } else {
+    auto complete_callback = base::BindOnce(
+        &PrefetchDispatcherImpl::ThumbnailFetchComplete, base::Unretained(this),
+        offline_id, std::move(remaining_ids));
+    service_->GetThumbnailFetcher()->FetchSuggestionImageData(
+        client_id, std::move(complete_callback));
+  }
+}
+
+void PrefetchDispatcherImpl::ThumbnailFetchComplete(
+    const int64_t offline_id,
+    std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,
+    const std::string& image_data) {
+  // Thumbnails are marked to expire after this delta. Expired thumbnails are
+  // eventually deleted if their offline_id does not correspond to an offline
+  // item. Two days gives us plenty of time so that the prefetched item can be
+  // imported into the offline item database.
+  const base::TimeDelta kThumbnailExpirationDelta =
+      base::TimeDelta::FromDays(2);
+
+  if (!image_data.empty()) {
+    service_->GetOfflinePageModel()->StoreThumbnail(OfflinePageThumbnail(
+        offline_id, base::Time::Now() + kThumbnailExpirationDelta, image_data));
+  }
+  FetchThumbnails(std::move(remaining_ids));
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
index 52535b168..8f514c94 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h
@@ -45,6 +45,7 @@
       const std::set<std::string>& outstanding_download_ids,
       const std::map<std::string, std::pair<base::FilePath, int64_t>>&
           success_downloads) override;
+  void GeneratePageBundleRequested(std::unique_ptr<IdsVector> ids) override;
   void DownloadCompleted(
       const PrefetchDownloadResult& download_result) override;
   void ItemDownloaded(int64_t offline_id, const ClientId& client_id) override;
@@ -82,6 +83,23 @@
   // becomes idle and any task called SchedulePipelineProcessing() before.
   void QueueActionTasks();
 
+  // The methods below control the  downloading of thumbnails for the provided
+  // prefetch items IDs. They are called multiple times for the same article,
+  // when they reach different points in the pipeline to increase the likeliness
+  // of the thumbnail to be available. The existence of the thumbnail is
+  // verified to avoid re-downloads.
+  // Also, even though unlikely, concurrent calls to these methods are
+  // supported. They will generate simultaneous download attempts but there will
+  // be no impact in the consistency of stored data.
+  void FetchThumbnails(std::unique_ptr<IdsVector> remaining_ids);
+  void ThumbnailExistenceChecked(const int64_t offline_id,
+                                 ClientId client_id,
+                                 std::unique_ptr<IdsVector> remaining_ids,
+                                 bool thumbnail_exists);
+  void ThumbnailFetchComplete(const int64_t offline_id,
+                              std::unique_ptr<IdsVector> remaining_ids,
+                              const std::string& image_data);
+
   PrefetchService* service_;
   TaskQueue task_queue_;
   bool needs_pipeline_processing_ = false;
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
index 772938a..4872567 100644
--- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -35,22 +35,32 @@
 #include "url/gurl.h"
 
 using testing::Contains;
+using ::testing::InSequence;
 
 namespace offline_pages {
 
 namespace {
 using testing::_;
 
-const char kTestNamespace[] = "TestPrefetchClientNamespace";
 const char kTestID[] = "id";
 const GURL kTestURL("https://www.chromium.org");
 const GURL kTestURL2("https://www.chromium.org/2");
 const int64_t kTestOfflineID = 1111;
-const char kClientID1[] = "client-id-1";
+const char kClientID[] = "client-id-1";
 
 class MockOfflinePageModel : public StubOfflinePageModel {
  public:
+  MockOfflinePageModel() = default;
+  ~MockOfflinePageModel() override = default;
   MOCK_METHOD1(StoreThumbnail, void(const OfflinePageThumbnail& thumb));
+  MOCK_METHOD2(HasThumbnailForOfflineId_,
+               void(int64_t offline_id,
+                    base::OnceCallback<void(bool)>* callback));
+  void HasThumbnailForOfflineId(
+      int64_t offline_id,
+      base::OnceCallback<void(bool)> callback) override {
+    HasThumbnailForOfflineId_(offline_id, &callback);
+  }
 };
 
 class TestPrefetchBackgroundTask : public PrefetchBackgroundTask {
@@ -125,10 +135,11 @@
     return reschedule_type_;
   }
 
-  void ExpectFetchThumbnail(const std::string& thumbnail_data) {
+  void ExpectFetchThumbnail(const std::string& thumbnail_data,
+                            const char* client_id = kClientID) {
     EXPECT_CALL(*thumbnail_fetcher_,
                 FetchSuggestionImageData_(
-                    ClientId(kSuggestedArticlesNamespace, kClientID1), _))
+                    ClientId(kSuggestedArticlesNamespace, client_id), _))
         .WillOnce(
             testing::Invoke(testing::CallbackToFunctor(base::BindRepeating(
                 [](const std::string& thumbnail_data,
@@ -142,12 +153,30 @@
                 thumbnail_data, task_runner()))));
   }
 
+  void ExpectHasThumbnailForOfflineId(int64_t offline_id, bool to_return) {
+    EXPECT_CALL(*offline_model_, HasThumbnailForOfflineId_(offline_id, _))
+        .WillOnce(
+            testing::Invoke(testing::CallbackToFunctor(base::BindRepeating(
+                [](bool to_return,
+                   scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
+                   int64_t offline_id_,
+                   base::OnceCallback<void(bool)>* callback) {
+                  task_runner->PostTask(
+                      FROM_HERE,
+                      base::BindOnce(std::move(*callback), to_return));
+                },
+                to_return, task_runner()))));
+  }
+
  protected:
   // Owned by |taco_|.
   MockOfflinePageModel* offline_model_;
 
   std::vector<PrefetchURL> test_urls_;
 
+  // Owned by |taco_|.
+  MockThumbnailFetcher* thumbnail_fetcher_;
+
  private:
   std::unique_ptr<PrefetchServiceTestTaco> taco_;
 
@@ -157,8 +186,6 @@
   PrefetchDispatcherImpl* dispatcher_;
   // Owned by |taco_|.
   TestPrefetchNetworkRequestFactory* network_request_factory_;
-  // Owned by |taco_|.
-  MockThumbnailFetcher* thumbnail_fetcher_;
 
   bool reschedule_called_ = false;
   PrefetchBackgroundTaskRescheduleType reschedule_type_ =
@@ -218,7 +245,8 @@
   // with the state of adding tasks, and that the end state is we have tests
   // that verify the proper tasks were added in the proper order at each wakeup
   // signal of the dispatcher.
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                  test_urls_);
   prefetch_dispatcher()->RemoveAllUnprocessedPrefetchURLs(
       kSuggestedArticlesNamespace);
   prefetch_dispatcher()->RemovePrefetchURLsByClientId(
@@ -226,7 +254,8 @@
 }
 
 TEST_F(PrefetchDispatcherTest, AddCandidatePrefetchURLsTask) {
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                  test_urls_);
   EXPECT_TRUE(dispatcher_task_queue()->HasPendingTasks());
   RunUntilIdle();
   EXPECT_FALSE(dispatcher_task_queue()->HasPendingTasks());
@@ -234,10 +263,11 @@
 }
 
 TEST_F(PrefetchDispatcherTest, RemovePrefetchURLsByClientId) {
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                  test_urls_);
   RunUntilIdle();
   prefetch_dispatcher()->RemovePrefetchURLsByClientId(
-      ClientId(kTestNamespace, test_urls_.front().id));
+      ClientId(kSuggestedArticlesNamespace, test_urls_.front().id));
   EXPECT_TRUE(dispatcher_task_queue()->HasPendingTasks());
   RunUntilIdle();
   EXPECT_FALSE(dispatcher_task_queue()->HasPendingTasks());
@@ -249,7 +279,8 @@
   disabled_feature_list.InitAndDisableFeature(kPrefetchingOfflinePagesFeature);
 
   // Don't add a task for new prefetch URLs.
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                  test_urls_);
   EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
 
   // Do nothing with a new background task.
@@ -263,7 +294,8 @@
   DisablePrefetchingInSettings();
 
   // Don't add a task for new prefetch URLs.
-  prefetch_dispatcher()->AddCandidatePrefetchURLs(kTestNamespace, test_urls_);
+  prefetch_dispatcher()->AddCandidatePrefetchURLs(kSuggestedArticlesNamespace,
+                                                  test_urls_);
   EXPECT_FALSE(dispatcher_task_queue()->HasRunningTask());
 
   // Do nothing with a new background task.
@@ -276,7 +308,7 @@
 TEST_F(PrefetchDispatcherTest, DispatcherReleasesBackgroundTask) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   // We start the background task, causing reconcilers and action tasks to be
@@ -307,7 +339,7 @@
 TEST_F(PrefetchDispatcherTest, RetryWithBackoffAfterFailedNetworkRequest) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   BeginBackgroundTask();
@@ -316,7 +348,7 @@
   // Trigger another request to make sure we have more work to do.
   PrefetchURL prefetch_url2(kTestID, kTestURL2, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
   RunUntilIdle();
 
   // This should trigger retry with backoff.
@@ -339,7 +371,7 @@
 TEST_F(PrefetchDispatcherTest, RetryWithoutBackoffAfterFailedNetworkRequest) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   BeginBackgroundTask();
@@ -348,7 +380,7 @@
   // Trigger another request to make sure we have more work to do.
   PrefetchURL prefetch_url2(kTestID, kTestURL2, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
 
   // This should trigger retry without backoff.
   RespondWithNetError(net::ERR_CONNECTION_CLOSED);
@@ -370,7 +402,7 @@
 TEST_F(PrefetchDispatcherTest, SuspendAfterFailedNetworkRequest) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   BeginBackgroundTask();
@@ -379,7 +411,7 @@
   // Trigger another request to make sure we have more work to do.
   PrefetchURL prefetch_url2(kTestID, kTestURL2, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
 
   EXPECT_FALSE(dispatcher_suspended());
 
@@ -405,7 +437,7 @@
 TEST_F(PrefetchDispatcherTest, SuspendRemovedAfterNewBackgroundTask) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   BeginBackgroundTask();
@@ -431,7 +463,7 @@
   // Trigger another request to make sure we have more work to do.
   PrefetchURL prefetch_url2(kTestID, kTestURL2, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url2));
 
   BeginBackgroundTask();
 
@@ -445,26 +477,70 @@
 TEST_F(PrefetchDispatcherTest, NoNetworkRequestsAfterNewURLs) {
   PrefetchURL prefetch_url(kTestID, kTestURL, base::string16());
   prefetch_dispatcher()->AddCandidatePrefetchURLs(
-      kTestNamespace, std::vector<PrefetchURL>(1, prefetch_url));
+      kSuggestedArticlesNamespace, std::vector<PrefetchURL>(1, prefetch_url));
   RunUntilIdle();
 
   // We should not have started GPB
   EXPECT_EQ(nullptr, GetRunningFetcher());
 }
 
-TEST_F(PrefetchDispatcherTest, ThumbnailFetchFailure) {
+TEST_F(PrefetchDispatcherTest, ThumbnailFetchFailure_ItemDownloaded) {
   ExpectFetchThumbnail("");
+  ExpectHasThumbnailForOfflineId(kTestOfflineID, false);
   EXPECT_CALL(*offline_model_, StoreThumbnail(_)).Times(0);
   prefetch_dispatcher()->ItemDownloaded(
-      kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID1));
+      kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID));
 }
 
-TEST_F(PrefetchDispatcherTest, ThumbnailFetchSuccess) {
+TEST_F(PrefetchDispatcherTest, ThumbnailFetchSuccess_ItemDownloaded) {
   std::string kThumbnailData = "abc";
+  ExpectHasThumbnailForOfflineId(kTestOfflineID, false);
   EXPECT_CALL(*offline_model_, StoreThumbnail(ValidThumbnail()));
   ExpectFetchThumbnail(kThumbnailData);
   prefetch_dispatcher()->ItemDownloaded(
-      kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID1));
+      kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID));
+}
+
+TEST_F(PrefetchDispatcherTest, ThumbnailAlreadyExists_ItemDownloaded) {
+  ExpectHasThumbnailForOfflineId(kTestOfflineID, true);
+  EXPECT_CALL(*thumbnail_fetcher_, FetchSuggestionImageData_(_, _)).Times(0);
+  EXPECT_CALL(*offline_model_, StoreThumbnail(_)).Times(0);
+  prefetch_dispatcher()->ItemDownloaded(
+      kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID));
+}
+
+TEST_F(PrefetchDispatcherTest,
+       ThumbnailVariousCases_GeneratePageBundleRequested) {
+  // Covers all possible thumbnail cases with a single
+  // GeneratePageBundleRequested call: fetch succeeds (#1), fetch fails (#2),
+  // item already exists (#3).
+  const int64_t kTestOfflineID1 = 100;
+  const int64_t kTestOfflineID2 = 101;
+  const int64_t kTestOfflineID3 = 102;
+  const char kClientID1[] = "a";
+  const char kClientID2[] = "b";
+  const char kClientID3[] = "c";
+
+  InSequence in_sequence;
+  // Case #1.
+  ExpectHasThumbnailForOfflineId(kTestOfflineID1, false);
+  ExpectFetchThumbnail("abc", kClientID1);
+  EXPECT_CALL(*offline_model_, StoreThumbnail(_)).Times(1);
+  // Case #2.
+  ExpectHasThumbnailForOfflineId(kTestOfflineID2, false);
+  ExpectFetchThumbnail("", kClientID2);
+  // Case #3.
+  ExpectHasThumbnailForOfflineId(kTestOfflineID3, true);
+
+  auto prefetch_item_ids = std::make_unique<PrefetchDispatcher::IdsVector>();
+  prefetch_item_ids->emplace_back(
+      kTestOfflineID1, ClientId(kSuggestedArticlesNamespace, kClientID1));
+  prefetch_item_ids->emplace_back(
+      kTestOfflineID2, ClientId(kSuggestedArticlesNamespace, kClientID2));
+  prefetch_item_ids->emplace_back(
+      kTestOfflineID3, ClientId(kSuggestedArticlesNamespace, kClientID3));
+  prefetch_dispatcher()->GeneratePageBundleRequested(
+      std::move(prefetch_item_ids));
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
index a535ae5..f968c35a 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
+++ b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.cc
@@ -59,6 +59,12 @@
   cleanup_downloads_count++;
 }
 
+void TestPrefetchDispatcher::GeneratePageBundleRequested(
+    std::unique_ptr<IdsVector> ids) {
+  generate_page_bundle_requested++;
+  ids_from_generate_page_bundle_requested = std::move(ids);
+}
+
 void TestPrefetchDispatcher::DownloadCompleted(
     const PrefetchDownloadResult& download_result) {
   download_results.push_back(download_result);
diff --git a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
index 4ca398b..e1aa62e 100644
--- a/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
+++ b/components/offline_pages/core/prefetch/test_prefetch_dispatcher.h
@@ -39,6 +39,7 @@
       const std::set<std::string>& outstanding_download_ids,
       const std::map<std::string, std::pair<base::FilePath, int64_t>>&
           success_downloads) override;
+  void GeneratePageBundleRequested(std::unique_ptr<IdsVector> ids) override;
   void DownloadCompleted(
       const PrefetchDownloadResult& download_result) override;
   void ItemDownloaded(int64_t offline_id, const ClientId& client_id) override;
@@ -51,6 +52,7 @@
   std::vector<PrefetchDownloadResult> download_results;
   std::vector<std::pair<int64_t, ClientId>> item_downloaded_results;
   std::vector<std::pair<int64_t, bool>> import_results;
+  std::unique_ptr<IdsVector> ids_from_generate_page_bundle_requested;
 
   int cleanup_downloads_count = 0;
   int new_suggestions_count = 0;
@@ -58,6 +60,7 @@
   int remove_all_suggestions_count = 0;
   int remove_by_client_id_count = 0;
   int task_schedule_count = 0;
+  int generate_page_bundle_requested = 0;
 };
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/stub_offline_page_model.cc b/components/offline_pages/core/stub_offline_page_model.cc
index 80ad5689..e3f3e2e2 100644
--- a/components/offline_pages/core/stub_offline_page_model.cc
+++ b/components/offline_pages/core/stub_offline_page_model.cc
@@ -71,6 +71,9 @@
 void StubOfflinePageModel::GetThumbnailByOfflineId(
     int64_t offline_id,
     GetThumbnailCallback callback) {}
+void StubOfflinePageModel::HasThumbnailForOfflineId(
+    int64_t offline_id,
+    base::OnceCallback<void(bool)> callback) {}
 void StubOfflinePageModel::PublishInternalArchive(
     const OfflinePageItem& offline_page,
     std::unique_ptr<OfflinePageArchiver> archiver,
diff --git a/components/offline_pages/core/stub_offline_page_model.h b/components/offline_pages/core/stub_offline_page_model.h
index 2a64463a..d923c704 100644
--- a/components/offline_pages/core/stub_offline_page_model.h
+++ b/components/offline_pages/core/stub_offline_page_model.h
@@ -74,6 +74,9 @@
   void StoreThumbnail(const OfflinePageThumbnail& thumb) override;
   void GetThumbnailByOfflineId(int64_t offline_id,
                                GetThumbnailCallback callback) override;
+  void HasThumbnailForOfflineId(
+      int64_t offline_id,
+      base::OnceCallback<void(bool)> callback) override;
   void PublishInternalArchive(
       const OfflinePageItem& offline_page,
       std::unique_ptr<OfflinePageArchiver> archiver,
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index a893da9..765dd1b5 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -65,6 +65,8 @@
     "clipboard_url_provider.h",
     "contextual_suggestions_service.cc",
     "contextual_suggestions_service.h",
+    "favicon_cache.cc",
+    "favicon_cache.h",
     "history_match.cc",
     "history_match.h",
     "history_provider.cc",
@@ -152,6 +154,8 @@
     "//base:i18n",
     "//components/bookmarks/browser",
     "//components/data_use_measurement/core",
+    "//components/favicon/core",
+    "//components/favicon_base",
     "//components/keyed_service/core",
     "//components/metrics",
     "//components/navigation_metrics",
@@ -284,6 +288,7 @@
     "bookmark_provider_unittest.cc",
     "builtin_provider_unittest.cc",
     "clipboard_url_provider_unittest.cc",
+    "favicon_cache_unittest.cc",
     "history_provider_unittest.cc",
     "history_quick_provider_unittest.cc",
     "history_url_provider_unittest.cc",
@@ -314,6 +319,7 @@
     "//base",
     "//components/bookmarks/browser",
     "//components/bookmarks/test",
+    "//components/favicon/core/test:test_support",
     "//components/history/core/test",
     "//components/open_from_clipboard:test_support",
     "//components/prefs:test_support",
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS
index dbc9c08..dbca7cf6 100644
--- a/components/omnibox/browser/DEPS
+++ b/components/omnibox/browser/DEPS
@@ -2,6 +2,8 @@
   "+components/bookmarks/browser",
   "+components/bookmarks/test",
   "+components/data_use_measurement/core",
+  "+components/favicon_base",
+  "+components/favicon/core",
   "+components/history/core/browser",
   "+components/history/core/test",
   "+components/keyed_service/core",
diff --git a/chrome/browser/ui/omnibox/favicon_cache.cc b/components/omnibox/browser/favicon_cache.cc
similarity index 98%
rename from chrome/browser/ui/omnibox/favicon_cache.cc
rename to components/omnibox/browser/favicon_cache.cc
index 1e605026..937e294 100644
--- a/chrome/browser/ui/omnibox/favicon_cache.cc
+++ b/components/omnibox/browser/favicon_cache.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/omnibox/favicon_cache.h"
+#include "components/omnibox/browser/favicon_cache.h"
 
 #include "base/containers/mru_cache.h"
 #include "components/favicon/core/favicon_service.h"
diff --git a/chrome/browser/ui/omnibox/favicon_cache.h b/components/omnibox/browser/favicon_cache.h
similarity index 87%
rename from chrome/browser/ui/omnibox/favicon_cache.h
rename to components/omnibox/browser/favicon_cache.h
index 9ec0f85..35ede6b7 100644
--- a/chrome/browser/ui/omnibox/favicon_cache.h
+++ b/components/omnibox/browser/favicon_cache.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_OMNIBOX_FAVICON_CACHE_H_
-#define CHROME_BROWSER_UI_OMNIBOX_FAVICON_CACHE_H_
+#ifndef COMPONENTS_OMNIBOX_BROWSER_FAVICON_CACHE_H_
+#define COMPONENTS_OMNIBOX_BROWSER_FAVICON_CACHE_H_
 
 #include <list>
 #include <map>
@@ -33,8 +33,10 @@
 typedef base::OnceCallback<void(const gfx::Image& favicon)>
     FaviconFetchedCallback;
 
-// We cache a very small number of favicons so we can synchronously deliver
-// them to prevent flicker as the user types.
+// This caches favicons for pages. We cache a small number of them so we can
+// synchronously deliver them to the UI to prevent flicker as the user types.
+// It also stores and times out null results from when we cannot fetch a favicon
+// from the history database.
 class FaviconCache : public history::HistoryServiceObserver {
  public:
   FaviconCache(favicon::FaviconService* favicon_service,
@@ -92,4 +94,4 @@
   DISALLOW_COPY_AND_ASSIGN(FaviconCache);
 };
 
-#endif  // CHROME_BROWSER_UI_OMNIBOX_FAVICON_CACHE_H_
+#endif  // COMPONENTS_OMNIBOX_BROWSER_FAVICON_CACHE_H_
diff --git a/chrome/browser/ui/omnibox/favicon_cache_unittest.cc b/components/omnibox/browser/favicon_cache_unittest.cc
similarity index 98%
rename from chrome/browser/ui/omnibox/favicon_cache_unittest.cc
rename to components/omnibox/browser/favicon_cache_unittest.cc
index aa9250e..1a7998fe 100644
--- a/chrome/browser/ui/omnibox/favicon_cache_unittest.cc
+++ b/components/omnibox/browser/favicon_cache_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 "chrome/browser/ui/omnibox/favicon_cache.h"
+#include "components/omnibox/browser/favicon_cache.h"
 
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/omnibox/browser/omnibox_view.h b/components/omnibox/browser/omnibox_view.h
index a7b6d95..91d803a 100644
--- a/components/omnibox/browser/omnibox_view.h
+++ b/components/omnibox/browser/omnibox_view.h
@@ -116,7 +116,7 @@
   // preserving and selecting the user's text if they already typed in a query.
   virtual void EnterKeywordModeForDefaultSearchProvider() = 0;
 
-  // Returns true if all text is selected or there is no text at all.
+  // Returns true if all text is selected. Returns false if there is no text.
   virtual bool IsSelectAll() const = 0;
 
   // Fills |start| and |end| with the indexes of the current selection's bounds.
diff --git a/components/omnibox_strings_grdp/OWNERS b/components/omnibox_strings_grdp/OWNERS
new file mode 100644
index 0000000..5535cd2
--- /dev/null
+++ b/components/omnibox_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/omnibox/OWNERS
diff --git a/components/omnibox_strings_grdp/README.md b/components/omnibox_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/omnibox_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index b8e680932..9d8345c0 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -215,6 +215,9 @@
     <message name="IDS_PAGE_INFO_TYPE_POPUPS" desc="The label used for popups permission controls in the Page Info popup.">
       Popups
     </message>
+    <message name="IDS_PAGE_INFO_TYPE_POPUPS_REDIRECTS" desc="The label used for the popup/redirect permission controls in the Page Info popup.">
+      Pop-ups and redirects
+    </message>
     <message name="IDS_PAGE_INFO_TYPE_FLASH" desc="The label used for Flash permissions in the Page Info popup.">
       Flash
     </message>
diff --git a/components/page_info_strings_grdp/OWNERS b/components/page_info_strings_grdp/OWNERS
new file mode 100644
index 0000000..7c601fd
--- /dev/null
+++ b/components/page_info_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/page_info/OWNERS
diff --git a/components/page_info_strings_grdp/README.md b/components/page_info_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/page_info_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index a1bc1aca..0b14d3a 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/syslog_logging.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/password_form.h"
@@ -329,6 +330,8 @@
   // about:blank frames as well as data URLs.  If that's not the case, kill the
   // renderer, as it might be exploited.
   if (url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kDataScheme)) {
+    SYSLOG(WARNING) << "Killing renderer: illegal password access from about: "
+                    << " or data: URL. Reason: " << static_cast<int>(reason);
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(), reason);
     return false;
   }
@@ -337,6 +340,8 @@
       content::ChildProcessSecurityPolicy::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_frame_host_->GetProcess()->GetID(),
                                       url)) {
+    SYSLOG(WARNING) << "Killing renderer: illegal password access. Reason: "
+                    << static_cast<int>(reason);
     bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(), reason);
     return false;
   }
diff --git a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
index a2b7363..7b77ba19 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
@@ -185,6 +185,11 @@
         AffiliatedMatchHelper::kInitializationDelayOnStartup);
   }
 
+  void ExpectNoDeferredTasks() {
+    mock_time_task_runner_->RunUntilIdle();
+    ASSERT_FALSE(mock_time_task_runner_->HasPendingTask());
+  }
+
   void RunUntilIdle() {
     // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of
     // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper
@@ -666,7 +671,7 @@
   match_helper()->Initialize();
   RunUntilIdle();
   DestroyMatchHelper();
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+  ASSERT_NO_FATAL_FAILURE(ExpectNoDeferredTasks());
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc b/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc
index 016b378..8c044d6 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler_unittest.cc
@@ -411,7 +411,8 @@
 
   throttler->SignalNetworkRequestNeeded();
   throttler.reset();
-  EXPECT_EQ(1u, GetPendingTaskCount());
+  // We expect the task to be cancelled.
+  EXPECT_EQ(0u, GetPendingTaskCount());
   AssertNoReleaseUntilNoTasksRemain();
 }
 
diff --git a/components/payments_strings_grdp/OWNERS b/components/payments_strings_grdp/OWNERS
new file mode 100644
index 0000000..616d5533
--- /dev/null
+++ b/components/payments_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/payments/OWNERS
diff --git a/components/payments_strings_grdp/README.md b/components/payments_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/payments_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/pdf_strings_grdp/OWNERS b/components/pdf_strings_grdp/OWNERS
new file mode 100644
index 0000000..db781ac
--- /dev/null
+++ b/components/pdf_strings_grdp/OWNERS
@@ -0,0 +1 @@
+raymes@chromium.org
diff --git a/components/pdf_strings_grdp/README.md b/components/pdf_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/pdf_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/policy_strings_grdp/README.md b/components/policy_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/policy_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/printing/service/pdf_compositor_service.cc b/components/printing/service/pdf_compositor_service.cc
index f0b8c7b0..d3dfcc4fe 100644
--- a/components/printing/service/pdf_compositor_service.cc
+++ b/components/printing/service/pdf_compositor_service.cc
@@ -66,7 +66,7 @@
 void PdfCompositorService::PrepareToStart() {
   // Set up discardable memory manager.
   discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
 #if defined(USE_AURA)
     context()->connector()->BindInterface(ui::mojom::kServiceName,
                                           &manager_ptr);
diff --git a/components/printing_strings_grdp/OWNERS b/components/printing_strings_grdp/OWNERS
new file mode 100644
index 0000000..21ccd7b
--- /dev/null
+++ b/components/printing_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/printing/OWNERS
diff --git a/components/printing_strings_grdp/README.md b/components/printing_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/printing_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/reset_password_strings_grdp/OWNERS b/components/reset_password_strings_grdp/OWNERS
new file mode 100644
index 0000000..5225674
--- /dev/null
+++ b/components/reset_password_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/safe_browsing/OWNERS
diff --git a/components/reset_password_strings_grdp/README.md b/components/reset_password_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/reset_password_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/safe_browsing/browser/safe_browsing_network_context.cc b/components/safe_browsing/browser/safe_browsing_network_context.cc
index aaad13c..b331202 100644
--- a/components/safe_browsing/browser/safe_browsing_network_context.cc
+++ b/components/safe_browsing/browser/safe_browsing_network_context.cc
@@ -41,6 +41,13 @@
     return network_context_.get();
   }
 
+  void FlushForTesting() {
+    if (network_context_)
+      network_context_.FlushForTesting();
+    if (url_loader_factory_)
+      url_loader_factory_.FlushForTesting();
+  }
+
  protected:
   // network::URLLoaderFactory implementation:
   void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader,
@@ -150,6 +157,11 @@
   return url_loader_factory_->GetNetworkContext();
 }
 
+void SafeBrowsingNetworkContext::FlushForTesting() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  url_loader_factory_->FlushForTesting();
+}
+
 void SafeBrowsingNetworkContext::ServiceShuttingDown() {
   url_loader_factory_->Reset();
 }
diff --git a/components/safe_browsing/browser/safe_browsing_network_context.h b/components/safe_browsing/browser/safe_browsing_network_context.h
index d558ef1..589c231 100644
--- a/components/safe_browsing/browser/safe_browsing_network_context.h
+++ b/components/safe_browsing/browser/safe_browsing_network_context.h
@@ -39,6 +39,9 @@
   // Returns a NetworkContext.
   network::mojom::NetworkContext* GetNetworkContext();
 
+  // Flushes NetworkContext and URLLoaderFactory pipes.
+  void FlushForTesting();
+
   // Called at shutdown to ensure that the URLRequestContextGetter reference is
   // destroyed..
   void ServiceShuttingDown();
diff --git a/components/security_state_strings_grdp/OWNERS b/components/security_state_strings_grdp/OWNERS
new file mode 100644
index 0000000..9e98e48
--- /dev/null
+++ b/components/security_state_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/security_state/OWNERS
diff --git a/components/security_state_strings_grdp/README.md b/components/security_state_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/security_state_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/ssl_errors_strings_grdp/OWNERS b/components/ssl_errors_strings_grdp/OWNERS
new file mode 100644
index 0000000..877ebf5a
--- /dev/null
+++ b/components/ssl_errors_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/ssl_errors/OWNERS
diff --git a/components/ssl_errors_strings_grdp/README.md b/components/ssl_errors_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/ssl_errors_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/supervised_user_error_page_strings_grdp/OWNERS b/components/supervised_user_error_page_strings_grdp/OWNERS
new file mode 100644
index 0000000..0ba46ac8
--- /dev/null
+++ b/components/supervised_user_error_page_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/supervised_user_error_page/OWNERS
diff --git a/components/supervised_user_error_page_strings_grdp/README.md b/components/supervised_user_error_page_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/supervised_user_error_page_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/sync_ui_strings_grdp/OWNERS b/components/sync_ui_strings_grdp/OWNERS
new file mode 100644
index 0000000..261ab18
--- /dev/null
+++ b/components/sync_ui_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/sync/OWNERS
diff --git a/components/sync_ui_strings_grdp/README.md b/components/sync_ui_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/sync_ui_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/translate_strings_grdp/OWNERS b/components/translate_strings_grdp/OWNERS
new file mode 100644
index 0000000..bb5c3afc
--- /dev/null
+++ b/components/translate_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/translate/OWNERS
diff --git a/components/translate_strings_grdp/README.md b/components/translate_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/translate_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/ukm/debug/ukm_debug_data_extractor.cc b/components/ukm/debug/ukm_debug_data_extractor.cc
index 89071bc..0e471c3 100644
--- a/components/ukm/debug/ukm_debug_data_extractor.cc
+++ b/components/ukm/debug/ukm_debug_data_extractor.cc
@@ -90,7 +90,7 @@
         for (const auto& metric : entry->metrics) {
           output.append(base::StringPrintf(
               "<h5>Metric:%s Value:%" PRId64 "</h5>",
-              GetName(it->second, metric->metric_hash).c_str(), metric->value));
+              GetName(it->second, metric.first).c_str(), metric.second));
         }
       }
     }
diff --git a/components/ukm/test_ukm_recorder.cc b/components/ukm/test_ukm_recorder.cc
index 8bb2a8cc..cdf89c4 100644
--- a/components/ukm/test_ukm_recorder.cc
+++ b/components/ukm/test_ukm_recorder.cc
@@ -29,7 +29,7 @@
     out->source_id = in->source_id;
   }
   for (const auto& metric : in->metrics) {
-    out->metrics.emplace_back(metric->Clone());
+    out->metrics.emplace(metric);
   }
 }
 
@@ -113,10 +113,9 @@
 const int64_t* TestUkmRecorder::GetEntryMetric(const mojom::UkmEntry* entry,
                                                base::StringPiece metric_name) {
   uint64_t hash = base::HashMetricName(metric_name);
-  for (const auto& metric : entry->metrics) {
-    if (metric->metric_hash == hash)
-      return &metric->value;
-  }
+  const auto it = entry->metrics.find(hash);
+  if (it != entry->metrics.end())
+    return &it->second;
   return nullptr;
 }
 
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc
index 69024974..88a91b8 100644
--- a/components/ukm/ukm_recorder_impl.cc
+++ b/components/ukm/ukm_recorder_impl.cc
@@ -120,8 +120,8 @@
   out->set_event_hash(in.event_hash);
   for (const auto& metric : in.metrics) {
     Entry::Metric* proto_metric = out->add_metrics();
-    proto_metric->set_metric_hash(metric->metric_hash);
-    proto_metric->set_value(metric->value);
+    proto_metric->set_metric_hash(metric.first);
+    proto_metric->set_value(metric.second);
   }
 }
 
@@ -157,7 +157,7 @@
     return true;
   const auto& metric_map = it->second.metric_map;
   for (const auto& metric : entry.metrics) {
-    if (metric_map.count(metric->metric_hash) == 0)
+    if (metric_map.count(metric.first) == 0)
       return true;
   }
   return false;
@@ -404,8 +404,8 @@
   EventAggregate& event_aggregate = event_aggregations_[entry->event_hash];
   event_aggregate.total_count++;
   for (const auto& metric : entry->metrics) {
-    MetricAggregate& aggregate = event_aggregate.metrics[metric->metric_hash];
-    double value = metric->value;
+    MetricAggregate& aggregate = event_aggregate.metrics[metric.first];
+    double value = metric.second;
     aggregate.total_count++;
     aggregate.value_sum += value;
     aggregate.value_square_sum += value * value;
@@ -420,7 +420,7 @@
     RecordDroppedEntry(DroppedDataReason::SAMPLED_OUT);
     event_aggregate.dropped_due_to_sampling++;
     for (auto& metric : entry->metrics)
-      event_aggregate.metrics[metric->metric_hash].dropped_due_to_sampling++;
+      event_aggregate.metrics[metric.first].dropped_due_to_sampling++;
     return;
   }
 
@@ -428,7 +428,7 @@
     RecordDroppedEntry(DroppedDataReason::MAX_HIT);
     event_aggregate.dropped_due_to_limits++;
     for (auto& metric : entry->metrics)
-      event_aggregate.metrics[metric->metric_hash].dropped_due_to_limits++;
+      event_aggregate.metrics[metric.first].dropped_due_to_limits++;
     return;
   }
 
diff --git a/components/undo_strings_grdp/OWNERS b/components/undo_strings_grdp/OWNERS
new file mode 100644
index 0000000..3c4cda0
--- /dev/null
+++ b/components/undo_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/undo/OWNERS
diff --git a/components/undo_strings_grdp/README.md b/components/undo_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/undo_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/version_ui_strings_grdp/OWNERS b/components/version_ui_strings_grdp/OWNERS
new file mode 100644
index 0000000..21929621
--- /dev/null
+++ b/components/version_ui_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/version_ui/OWNERS
diff --git a/components/version_ui_strings_grdp/README.md b/components/version_ui_strings_grdp/README.md
new file mode 100644
index 0000000..119d8b9
--- /dev/null
+++ b/components/version_ui_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/viz/common/display/renderer_settings.h b/components/viz/common/display/renderer_settings.h
index db0b1d7..740cb2a 100644
--- a/components/viz/common/display/renderer_settings.h
+++ b/components/viz/common/display/renderer_settings.h
@@ -33,6 +33,7 @@
   bool allow_overlays = true;
   bool dont_round_texture_sizes_for_pixel_tests = false;
   int highp_threshold_min = 0;
+  bool auto_resize_output_surface = true;
 
   int slow_down_compositing_scale_factor = 1;
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index a0ec8d1..38b20f4 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -105,6 +105,8 @@
     "display_embedder/server_shared_bitmap_manager.h",
     "display_embedder/skia_output_surface_impl.cc",
     "display_embedder/skia_output_surface_impl.h",
+    "display_embedder/skia_output_surface_impl_on_gpu.cc",
+    "display_embedder/skia_output_surface_impl_on_gpu.h",
     "display_embedder/software_output_surface.cc",
     "display_embedder/software_output_surface.h",
     "display_embedder/viz_process_context_provider.cc",
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 4470265..42b6787 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -315,7 +315,8 @@
   gfx::Size surface_size;
   bool have_damage = false;
   auto& last_render_pass = *frame.render_pass_list.back();
-  if (last_render_pass.output_rect.size() != current_surface_size_ &&
+  if (settings_.auto_resize_output_surface &&
+      last_render_pass.output_rect.size() != current_surface_size_ &&
       last_render_pass.damage_rect == last_render_pass.output_rect &&
       !current_surface_size_.IsEmpty()) {
     // Resize the output rect to the current surface size so that we won't
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 62df999e..c97a62e 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -805,10 +805,9 @@
   shader = content->makeShader(&content_mat);
 
   if (quad->mask_resource_id()) {
-    cc::DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
-        resource_provider_, quad->mask_resource_id());
-    const SkImage* image = mask_lock.sk_image();
-    if (!mask_lock.valid())
+    ScopedSkImageBuilder builder(this, quad->mask_resource_id());
+    const SkImage* image = builder.sk_image();
+    if (!image)
       return;
 
     // Scale normalized uv rect into absolute texel coordinates.
diff --git a/components/viz/service/display_embedder/output_device_backing_unittest.cc b/components/viz/service/display_embedder/output_device_backing_unittest.cc
index 6b24f28..91657333 100644
--- a/components/viz/service/display_embedder/output_device_backing_unittest.cc
+++ b/components/viz/service/display_embedder/output_device_backing_unittest.cc
@@ -15,7 +15,7 @@
 
 size_t GetViewportSizeInBytes(const gfx::Size& viewport_size) {
   size_t bytes = std::numeric_limits<size_t>::max();
-  DCHECK(ResourceSizes::MaybeSizeInBytes(viewport_size, RGBA_8888, &bytes));
+  CHECK(ResourceSizes::MaybeSizeInBytes(viewport_size, RGBA_8888, &bytes));
   return bytes;
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 8966c6d..08e427f70 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -4,7 +4,6 @@
 
 #include "components/viz/service/display_embedder/skia_output_surface_impl.h"
 
-#include "base/atomic_sequence_num.h"
 #include "base/callback_helpers.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -13,71 +12,45 @@
 #include "components/viz/common/resources/resource_metadata.h"
 #include "components/viz/service/display/output_surface_client.h"
 #include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
 #include "components/viz/service/display_embedder/viz_process_context_provider.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
-#include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/scheduler.h"
-#include "gpu/command_buffer/service/sync_point_manager.h"
-#include "gpu/command_buffer/service/texture_base.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "gpu/ipc/service/image_transport_surface.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_gl_api_implementation.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_switches_util.h"
-#include "ui/gl/gl_version_info.h"
 
 namespace viz {
+
 namespace {
 
-base::AtomicSequenceNumber g_next_command_buffer_id;
+template <typename... Args>
+void PostAsyncTask(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+    const base::RepeatingCallback<void(Args...)>& callback,
+    Args... args) {
+  task_runner->PostTask(FROM_HERE, base::BindOnce(callback, args...));
+}
+
+template <typename... Args>
+base::RepeatingCallback<void(Args...)> CreateSafeCallback(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+    const base::RepeatingCallback<void(Args...)>& callback) {
+  return base::BindRepeating(&PostAsyncTask<Args...>, task_runner, callback);
+}
 
 }  // namespace
 
-// Metadata for YUV promise SkImage.
-class SkiaOutputSurfaceImpl::YUVResourceMetadata {
- public:
-  YUVResourceMetadata(std::vector<ResourceMetadata> metadatas,
-                      SkYUVColorSpace yuv_color_space)
-      : metadatas_(std::move(metadatas)), yuv_color_space_(yuv_color_space) {
-    DCHECK(metadatas_.size() == 2 || metadatas_.size() == 3);
-  }
-  YUVResourceMetadata(YUVResourceMetadata&& other) = default;
-  ~YUVResourceMetadata() = default;
-  YUVResourceMetadata& operator=(YUVResourceMetadata&& other) = default;
-
-  const std::vector<ResourceMetadata>& metadatas() const { return metadatas_; }
-  SkYUVColorSpace yuv_color_space() const { return yuv_color_space_; }
-  const sk_sp<SkImage> image() const { return image_; }
-  void set_image(sk_sp<SkImage> image) { image_ = image; }
-  const gfx::Size size() const { return metadatas_[0].size; }
-
- private:
-  // Resource metadatas for YUV planes.
-  std::vector<ResourceMetadata> metadatas_;
-
-  SkYUVColorSpace yuv_color_space_;
-
-  // The image copied from YUV textures, it is for fullfilling the promise
-  // image.
-  // TODO(penghuang): Remove it when Skia supports drawing YUV textures
-  // directly.
-  sk_sp<SkImage> image_;
-
-  DISALLOW_COPY_AND_ASSIGN(YUVResourceMetadata);
-};
-
 // A helper class for fullfilling promise image on the GPU thread.
 template <class FullfillContextType>
 class SkiaOutputSurfaceImpl::PromiseTextureHelper {
  public:
   using HelperType = PromiseTextureHelper<FullfillContextType>;
 
-  PromiseTextureHelper(SkiaOutputSurfaceImpl* impl, FullfillContextType context)
-      : impl_(impl), context_(std::move(context)) {}
+  PromiseTextureHelper(base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
+                       FullfillContextType context)
+      : impl_on_gpu_(impl_on_gpu), context_(std::move(context)) {}
   ~PromiseTextureHelper() = default;
 
   static sk_sp<SkImage> MakePromiseSkImage(
@@ -91,8 +64,9 @@
       SkAlphaType alpha_type,
       sk_sp<SkColorSpace> color_space,
       FullfillContextType context) {
-    DCHECK_CALLED_ON_VALID_THREAD(impl->client_thread_checker_);
-    auto helper = std::make_unique<HelperType>(impl, std::move(context));
+    DCHECK_CALLED_ON_VALID_THREAD(impl->thread_checker_);
+    auto helper = std::make_unique<HelperType>(impl->impl_on_gpu_->weak_ptr(),
+                                               std::move(context));
     auto image = recorder->makePromiseTexture(
         backend_format, size.width(), size.height(), mip_mapped, origin,
         color_type, alpha_type, color_space, HelperType::Fullfill,
@@ -111,8 +85,12 @@
                        GrBackendTexture* backend_texture) {
     DCHECK(texture_context);
     auto* helper = static_cast<HelperType*>(texture_context);
-    DCHECK_CALLED_ON_VALID_THREAD(helper->impl_->gpu_thread_checker_);
-    helper->impl_->OnPromiseTextureFullfill(helper->context_, backend_texture);
+    // The fullfill is always called by SkiaOutputSurfaceImplOnGpu::SwapBuffers
+    // or SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass, so impl_on_gpu_
+    // should be always valid.
+    DCHECK(helper->impl_on_gpu_);
+    helper->impl_on_gpu_->FullfillPromiseTexture(helper->context_,
+                                                 backend_texture);
   }
 
   static void Release(void* texture_context) { DCHECK(texture_context); }
@@ -123,7 +101,7 @@
         static_cast<HelperType*>(texture_context));
   }
 
-  SkiaOutputSurfaceImpl* const impl_;
+  base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_;
 
   // The data for calling the fullfill methods in SkiaOutputSurfaceImpl.
   FullfillContextType context_;
@@ -141,9 +119,8 @@
 // TODO(penghuang): Remove this hack when Skia supports drawing YUV textures
 // directly.
 template <>
-void SkiaOutputSurfaceImpl::PromiseTextureHelper<
-    SkiaOutputSurfaceImpl::YUVResourceMetadata>::Init(SkiaOutputSurfaceImpl*
-                                                          impl) {
+void SkiaOutputSurfaceImpl::PromiseTextureHelper<YUVResourceMetadata>::Init(
+    SkiaOutputSurfaceImpl* impl) {
   impl->yuv_resource_metadatas_.push_back(&context_);
 }
 
@@ -153,41 +130,37 @@
     scoped_refptr<VizProcessContextProvider> context_provider,
     SyntheticBeginFrameSource* synthetic_begin_frame_source)
     : SkiaOutputSurface(context_provider),
-      command_buffer_id_(gpu::CommandBufferId::FromUnsafeValue(
-          g_next_command_buffer_id.GetNext() + 1)),
       gpu_service_(gpu_service),
       surface_handle_(surface_handle),
       synthetic_begin_frame_source_(synthetic_begin_frame_source),
-      gpu_thread_weak_ptr_factory_(this),
-      client_thread_weak_ptr_factory_(this) {
-  DETACH_FROM_THREAD(client_thread_checker_);
-  DETACH_FROM_THREAD(gpu_thread_checker_);
+      weak_ptr_factory_(this) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 SkiaOutputSurfaceImpl::~SkiaOutputSurfaceImpl() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   recorder_ = nullptr;
-  // TODO(penghuang): avoid blocking compositor thread.
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  // Use GPU scheduler to release impl_on_gpu_ on the GPU thread, so all
+  // scheduled tasks for the impl_on_gpu_ will be executed, before releasing
+  // it. The GPU thread is the main thread of the viz process. It outlives the
+  // compositor thread. We don't need worry about it for now.
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
-  auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::DestroyOnGpuThread,
-                                 base::Unretained(this), &event);
+  auto callback =
+      base::BindOnce([](std::unique_ptr<SkiaOutputSurfaceImplOnGpu>) {},
+                     std::move(impl_on_gpu_));
   gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
       sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
-  event.Wait();
 }
 
 void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(client);
   DCHECK(!client_);
 
   client_ = client;
-  client_thread_weak_ptr_ = client_thread_weak_ptr_factory_.GetWeakPtr();
+  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
   client_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
-  // TODO(penghuang): avoid blocking compositor thread.
   base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
@@ -199,12 +172,12 @@
 }
 
 void SkiaOutputSurfaceImpl::EnsureBackbuffer() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NOTIMPLEMENTED();
 }
 
 void SkiaOutputSurfaceImpl::DiscardBackbuffer() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NOTIMPLEMENTED();
 }
 
@@ -213,7 +186,7 @@
 }
 
 void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NOTIMPLEMENTED();
 }
 
@@ -222,7 +195,7 @@
                                     const gfx::ColorSpace& color_space,
                                     bool has_alpha,
                                     bool use_stencil) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   recorder_ = nullptr;
   SkSurfaceCharacterization* characterization = nullptr;
@@ -241,8 +214,10 @@
   }
 
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
-  auto callback = base::BindOnce(&SkiaOutputSurfaceImpl::ReshapeOnGpuThread,
-                                 base::Unretained(this), size,
+  // impl_on_gpu_ is released on the GPU thread by a posted task from
+  // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
+  auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
+                                 base::Unretained(impl_on_gpu_.get()), size,
                                  device_scale_factor, color_space, has_alpha,
                                  use_stencil, characterization, event.get());
   gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
@@ -258,14 +233,14 @@
 }
 
 uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   return GL_RGB;
 }
 
 OverlayCandidateValidator* SkiaOutputSurfaceImpl::GetOverlayCandidateValidator()
     const {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return nullptr;
 }
 
@@ -274,35 +249,35 @@
 }
 
 unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return 0;
 }
 
 gfx::BufferFormat SkiaOutputSurfaceImpl::GetOverlayBufferFormat() const {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return gfx::BufferFormat::RGBX_8888;
 }
 
 bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   return false;
 }
 
 void SkiaOutputSurfaceImpl::ApplyExternalStencil() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 #if BUILDFLAG(ENABLE_VULKAN)
 gpu::VulkanSurface* SkiaOutputSurfaceImpl::GetVulkanSurface() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   return nullptr;
 }
 #endif
 
 SkCanvas* SkiaOutputSurfaceImpl::GetSkCanvasForCurrentFrame() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
   DCHECK_EQ(current_render_pass_id_, 0u);
 
@@ -311,7 +286,7 @@
 
 sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImage(
     ResourceMetadata metadata) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
   // Convert internal format from GLES2 to platform GL.
@@ -333,7 +308,7 @@
 sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromYUV(
     std::vector<ResourceMetadata> metadatas,
     SkYUVColorSpace yuv_color_space) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
   DCHECK(metadatas.size() == 2 || metadatas.size() == 3);
@@ -355,21 +330,24 @@
 
 gpu::SyncToken SkiaOutputSurfaceImpl::SkiaSwapBuffers(
     OutputSurfaceFrame frame) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
-                            command_buffer_id_, ++sync_fence_release_);
+                            impl_on_gpu_->command_buffer_id(),
+                            ++sync_fence_release_);
   sync_token.SetVerifyFlush();
 
   auto ddl = recorder_->detach();
   DCHECK(ddl);
   RecreateRecorder();
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
-  auto callback =
-      base::BindOnce(&SkiaOutputSurfaceImpl::SwapBuffersOnGpuThread,
-                     base::Unretained(this), std::move(frame), std::move(ddl),
-                     std::move(yuv_resource_metadatas_), sync_fence_release_);
+  // impl_on_gpu_ is released on the GPU thread by a posted task from
+  // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
+  auto callback = base::BindOnce(
+      &SkiaOutputSurfaceImplOnGpu::SwapBuffers,
+      base::Unretained(impl_on_gpu_.get()), std::move(frame), std::move(ddl),
+      std::move(yuv_resource_metadatas_), sync_fence_release_);
   gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
       sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
   return sync_token;
@@ -380,7 +358,7 @@
     const gfx::Size& surface_size,
     ResourceFormat format,
     bool mipmap) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(gpu_service_->gr_context());
   DCHECK(!current_render_pass_id_);
   DCHECK(!offscreen_surface_recorder_);
@@ -416,27 +394,30 @@
 }
 
 gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintRenderPass() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(gpu_service_->gr_context());
   DCHECK(current_render_pass_id_);
   DCHECK(offscreen_surface_recorder_);
 
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
-                            command_buffer_id_, ++sync_fence_release_);
+                            impl_on_gpu_->command_buffer_id(),
+                            ++sync_fence_release_);
+  sync_token.SetVerifyFlush();
 
   auto ddl = offscreen_surface_recorder_->detach();
   offscreen_surface_recorder_ = nullptr;
   DCHECK(ddl);
 
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
+  // impl_on_gpu_ is released on the GPU thread by a posted task from
+  // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
   auto callback = base::BindOnce(
-      &SkiaOutputSurfaceImpl::FinishPaintRenderPassOnGpuThread,
-      base::Unretained(this), current_render_pass_id_, std::move(ddl),
-      std::move(yuv_resource_metadatas_), sync_fence_release_);
+      &SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass,
+      base::Unretained(impl_on_gpu_.get()), current_render_pass_id_,
+      std::move(ddl), std::move(yuv_resource_metadatas_), sync_fence_release_);
   gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
       sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
   current_render_pass_id_ = 0;
-  sync_token.SetVerifyFlush();
   return sync_token;
 }
 
@@ -445,7 +426,7 @@
     const gfx::Size& size,
     ResourceFormat format,
     bool mipmap) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
   // Convert internal format from GLES2 to platform GL.
@@ -466,264 +447,37 @@
     std::vector<RenderPassId> ids) {
   DCHECK(!ids.empty());
   auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
-  auto callback = base::BindOnce(
-      &SkiaOutputSurfaceImpl::RemoveRenderPassResourceOnGpuThread,
-      base::Unretained(this), std::move(ids));
+  // impl_on_gpu_ is released on the GPU thread by a posted task from
+  // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
+  auto callback =
+      base::BindOnce(&SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource,
+                     base::Unretained(impl_on_gpu_.get()), std::move(ids));
   gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
       sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
 }
 
-void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
-    gpu::SwapBuffersCompleteParams params) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  params.swap_response.swap_id = pending_swap_completed_ids_.front();
-  pending_swap_completed_ids_.pop_front();
-
-  client_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &SkiaOutputSurfaceImpl::DidSwapBuffersCompleteOnClientThread,
-          client_thread_weak_ptr_, params));
-}
-
-const gpu::gles2::FeatureInfo* SkiaOutputSurfaceImpl::GetFeatureInfo() const {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-const gpu::GpuPreferences& SkiaOutputSurfaceImpl::GetGpuPreferences() const {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  NOTIMPLEMENTED();
-  return gpu_preferences_;
-}
-
-void SkiaOutputSurfaceImpl::SetSnapshotRequestedCallback(
-    const base::Closure& callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  NOTIMPLEMENTED();
-}
-
-void SkiaOutputSurfaceImpl::UpdateVSyncParameters(base::TimeTicks timebase,
-                                                  base::TimeDelta interval) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  DCHECK(!gl::IsPresentationCallbackEnabled());
-
-  client_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &SkiaOutputSurfaceImpl::UpdateVSyncParametersOnClientThread,
-          client_thread_weak_ptr_, timebase, interval));
-}
-
-void SkiaOutputSurfaceImpl::BufferPresented(
-    const gfx::PresentationFeedback& feedback) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  DCHECK(gl::IsPresentationCallbackEnabled());
-  uint64_t swap_id = pending_presented_ids_.front();
-  pending_presented_ids_.pop_front();
-
-  client_thread_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SkiaOutputSurfaceImpl::BufferPresentedOnClientThread,
-                     client_thread_weak_ptr_, swap_id, feedback));
-}
-
-void SkiaOutputSurfaceImpl::AddFilter(IPC::MessageFilter* message_filter) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  NOTIMPLEMENTED();
-}
-
-int32_t SkiaOutputSurfaceImpl::GetRouteID() const {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  NOTIMPLEMENTED();
-  return 0;
-}
-
 void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-
   base::ScopedClosureRunner scoped_runner(
       base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
-
-  sync_point_client_state_ =
-      gpu_service_->sync_point_manager()->CreateSyncPointClientState(
-          gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_,
-          gpu_service_->skia_output_surface_sequence_id());
-
-  surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
-      gpu_thread_weak_ptr_factory_.GetWeakPtr(), surface_handle_,
-      gl::GLSurfaceFormat());
-  DCHECK(surface_);
-
-  if (!gpu_service_->CreateGrContextIfNecessary(surface_.get())) {
-    LOG(FATAL) << "Failed to create GrContext";
-    // TODO(penghuang): handle the failure.
-  }
-
-  DCHECK(gpu_service_->context_for_skia());
-  DCHECK(gpu_service_->gr_context());
-
-  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
-  }
-
-  capabilities_.flipped_output_surface = surface_->FlipsVertically();
-
-  // Get stencil bits from the default frame buffer.
-  auto* current_gl = gpu_service_->context_for_skia()->GetCurrentGL();
-  const auto* version = current_gl->Version;
-  auto* api = current_gl->Api;
-  GLint stencil_bits = 0;
-  if (version->is_desktop_core_profile) {
-    api->glGetFramebufferAttachmentParameterivEXTFn(
-        GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
-        &stencil_bits);
-  } else {
-    api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
-  }
-
-  capabilities_.supports_stencil = stencil_bits > 0;
-}
-
-void SkiaOutputSurfaceImpl::DestroyOnGpuThread(base::WaitableEvent* event) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-
-  base::ScopedClosureRunner scoped_runner(
-      base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
-  gpu_thread_weak_ptr_factory_.InvalidateWeakPtrs();
-
-  // Destroy the surface with the context current, some surface destructors
-  // make GL calls.
-  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
-  }
-  surface_ = nullptr;
-  sync_point_client_state_ = nullptr;
-}
-
-void SkiaOutputSurfaceImpl::ReshapeOnGpuThread(
-    const gfx::Size& size,
-    float device_scale_factor,
-    const gfx::ColorSpace& color_space,
-    bool has_alpha,
-    bool use_stencil,
-    SkSurfaceCharacterization* characterization,
-    base::WaitableEvent* event) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-
-  std::unique_ptr<base::ScopedClosureRunner> scoped_runner;
-  if (event) {
-    scoped_runner = std::make_unique<base::ScopedClosureRunner>(
-        base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
-  }
-
-  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
-  }
-  gl::GLSurface::ColorSpace surface_color_space =
-      color_space == gfx::ColorSpace::CreateSCRGBLinear()
-          ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
-          : gl::GLSurface::ColorSpace::UNSPECIFIED;
-  if (!surface_->Resize(size, device_scale_factor, surface_color_space,
-                        has_alpha)) {
-    LOG(FATAL) << "Failed to resize.";
-    // TODO(penghuang): Handle the failure.
-  }
-  DCHECK(gpu_service_->context_for_skia()->IsCurrent(surface_.get()));
-  DCHECK(gpu_service_->gr_context());
-
-  SkSurfaceProps surface_props =
-      SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
-
-  GrGLFramebufferInfo framebuffer_info;
-  framebuffer_info.fFBOID = 0;
-  const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
-  framebuffer_info.fFormat = version_info->is_es ? GL_BGRA8_EXT : GL_RGBA8;
-
-  GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
-                                      framebuffer_info);
-
-  sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
-      gpu_service_->gr_context(), render_target, kBottomLeft_GrSurfaceOrigin,
-      kBGRA_8888_SkColorType, nullptr, &surface_props);
-  DCHECK(sk_surface_);
-
-  if (characterization) {
-    sk_surface_->characterize(characterization);
-    DCHECK(characterization->isValid());
-  }
-}
-
-void SkiaOutputSurfaceImpl::SwapBuffersOnGpuThread(
-    OutputSurfaceFrame frame,
-    std::unique_ptr<SkDeferredDisplayList> ddl,
-    std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
-    uint64_t sync_fence_release) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  DCHECK(ddl);
-  DCHECK(sk_surface_);
-
-  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
-  }
-
-  PreprocessYUVResources(std::move(yuv_resource_metadatas));
-
-  sk_surface_->draw(ddl.get());
-  gpu_service_->gr_context()->flush();
-  OnSwapBuffers();
-  surface_->SwapBuffers(
-      base::BindRepeating([](const gfx::PresentationFeedback&) {}));
-  sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
-}
-
-void SkiaOutputSurfaceImpl::FinishPaintRenderPassOnGpuThread(
-    RenderPassId id,
-    std::unique_ptr<SkDeferredDisplayList> ddl,
-    std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
-    uint64_t sync_fence_release) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  DCHECK(ddl);
-
-  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle resize failure.
-  }
-
-  PreprocessYUVResources(std::move(yuv_resource_metadatas));
-
-  auto& surface = offscreen_surfaces_[id];
-  SkSurfaceCharacterization characterization;
-  // TODO(penghuang): Using characterization != ddl->characterization(), when
-  // the SkSurfaceCharacterization::operator!= is implemented in Skia.
-  if (!surface || !surface->characterize(&characterization) ||
-      characterization != ddl->characterization()) {
-    surface = SkSurface::MakeRenderTarget(
-        gpu_service_->gr_context(), ddl->characterization(), SkBudgeted::kNo);
-    DCHECK(surface);
-  }
-  surface->draw(ddl.get());
-  surface->flush();
-  sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
-}
-
-void SkiaOutputSurfaceImpl::RemoveRenderPassResourceOnGpuThread(
-    std::vector<RenderPassId> ids) {
-  DCHECK(!ids.empty());
-  for (const auto& id : ids) {
-    auto it = offscreen_surfaces_.find(id);
-    DCHECK(it != offscreen_surfaces_.end());
-    offscreen_surfaces_.erase(it);
-  }
+  auto did_swap_buffer_complete_callback = base::BindRepeating(
+      &SkiaOutputSurfaceImpl::DidSwapBuffersComplete, weak_ptr_);
+  auto update_vsync_parameters_callback = base::BindRepeating(
+      &SkiaOutputSurfaceImpl::UpdateVSyncParameters, weak_ptr_);
+  auto buffer_presented_callback =
+      base::BindRepeating(&SkiaOutputSurfaceImpl::BufferPresented, weak_ptr_);
+  impl_on_gpu_ = std::make_unique<SkiaOutputSurfaceImplOnGpu>(
+      gpu_service_, surface_handle_,
+      CreateSafeCallback(client_thread_task_runner_,
+                         did_swap_buffer_complete_callback),
+      CreateSafeCallback(client_thread_task_runner_,
+                         update_vsync_parameters_callback),
+      CreateSafeCallback(client_thread_task_runner_,
+                         buffer_presented_callback));
+  capabilities_ = impl_on_gpu_->capabilities();
 }
 
 void SkiaOutputSurfaceImpl::RecreateRecorder() {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(characterization_.isValid());
   recorder_ =
       std::make_unique<SkDeferredDisplayListRecorder>(characterization_);
@@ -732,83 +486,9 @@
   recorder_->getCanvas();
 }
 
-void SkiaOutputSurfaceImpl::PreprocessYUVResources(
-    std::vector<YUVResourceMetadata*> yuv_resource_metadatas) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-
-  // Create SkImage for fullfilling YUV promise image, before drawing the ddl.
-  // TODO(penghuang): Remove the extra step when Skia supports drawing YUV
-  // textures directly.
-  auto* mailbox_manager = gpu_service_->mailbox_manager();
-  for (auto* yuv_metadata : yuv_resource_metadatas) {
-    const auto& metadatas = yuv_metadata->metadatas();
-    DCHECK(metadatas.size() == 2 || metadatas.size() == 3);
-    GrBackendTexture backend_textures[3];
-    size_t i = 0;
-    for (const auto& metadata : metadatas) {
-      auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
-      if (!texture_base)
-        break;
-      BindOrCopyTextureIfNecessary(texture_base);
-      GrGLTextureInfo texture_info;
-      texture_info.fTarget = texture_base->target();
-      texture_info.fID = texture_base->service_id();
-      texture_info.fFormat = *metadata.backend_format.getGLFormat();
-      backend_textures[i++] =
-          GrBackendTexture(metadata.size.width(), metadata.size.height(),
-                           GrMipMapped::kNo, texture_info);
-    }
-
-    if (i != metadatas.size())
-      continue;
-
-    sk_sp<SkImage> image;
-    if (metadatas.size() == 2) {
-      image = SkImage::MakeFromNV12TexturesCopy(
-          gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
-          backend_textures, kTopLeft_GrSurfaceOrigin,
-          nullptr /* image_color_space */);
-      DCHECK(image);
-    } else {
-      image = SkImage::MakeFromYUVTexturesCopy(
-          gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
-          backend_textures, kTopLeft_GrSurfaceOrigin,
-          nullptr /* image_color_space */);
-      DCHECK(image);
-    }
-    yuv_metadata->set_image(std::move(image));
-  }
-}
-
-void SkiaOutputSurfaceImpl::BindOrCopyTextureIfNecessary(
-    gpu::TextureBase* texture_base) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  if (gpu_service_->gpu_preferences().use_passthrough_cmd_decoder)
-    return;
-
-  // If a texture created with non-passthrough command buffer and bind with
-  // an image, the Chrome will defer copying the image to the texture until
-  // the texture is used. It is for implementing low latency drawing and
-  // avoiding unnecessary texture copy. So we need check the texture image
-  // state, and bind or copy the image to the texture if necessary.
-  auto* texture = static_cast<gpu::gles2::Texture*>(texture_base);
-  gpu::gles2::Texture::ImageState image_state;
-  auto* image = texture->GetLevelImage(GL_TEXTURE_2D, 0, &image_state);
-  if (image && image_state == gpu::gles2::Texture::UNBOUND) {
-    glBindTexture(texture_base->target(), texture_base->service_id());
-    if (image->BindTexImage(texture_base->target())) {
-    } else {
-      texture->SetLevelImageState(texture_base->target(), 0,
-                                  gpu::gles2::Texture::COPIED);
-      if (!image->CopyTexImage(texture_base->target()))
-        LOG(ERROR) << "Failed to copy a gl image to texture.";
-    }
-  }
-}
-
-void SkiaOutputSurfaceImpl::DidSwapBuffersCompleteOnClientThread(
+void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
     gpu::SwapBuffersCompleteParams params) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(client_);
 
   if (!params.texture_in_use_responses.empty())
@@ -818,10 +498,9 @@
   client_->DidReceiveSwapBuffersAck(params.swap_response.swap_id);
 }
 
-void SkiaOutputSurfaceImpl::UpdateVSyncParametersOnClientThread(
-    base::TimeTicks timebase,
-    base::TimeDelta interval) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+void SkiaOutputSurfaceImpl::UpdateVSyncParameters(base::TimeTicks timebase,
+                                                  base::TimeDelta interval) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (synthetic_begin_frame_source_) {
     // TODO(brianderson): We should not be receiving 0 intervals.
@@ -831,67 +510,13 @@
   }
 }
 
-void SkiaOutputSurfaceImpl::BufferPresentedOnClientThread(
+void SkiaOutputSurfaceImpl::BufferPresented(
     uint64_t swap_id,
     const gfx::PresentationFeedback& feedback) {
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(client_);
 
   client_->DidReceivePresentationFeedback(swap_id, feedback);
 }
 
-void SkiaOutputSurfaceImpl::OnPromiseTextureFullfill(
-    const ResourceMetadata& metadata,
-    GrBackendTexture* backend_texture) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-  auto* mailbox_manager = gpu_service_->mailbox_manager();
-  auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
-  if (!texture_base) {
-    DLOG(ERROR) << "Failed to full fill the promise texture.";
-    return;
-  }
-  BindOrCopyTextureIfNecessary(texture_base);
-  GrGLTextureInfo texture_info;
-  texture_info.fTarget = texture_base->target();
-  texture_info.fID = texture_base->service_id();
-  texture_info.fFormat = *metadata.backend_format.getGLFormat();
-  *backend_texture =
-      GrBackendTexture(metadata.size.width(), metadata.size.height(),
-                       metadata.mip_mapped, texture_info);
-}
-
-void SkiaOutputSurfaceImpl::OnPromiseTextureFullfill(
-    const YUVResourceMetadata& yuv_metadata,
-    GrBackendTexture* backend_texture) {
-  DCHECK_CALLED_ON_VALID_THREAD(gpu_thread_checker_);
-
-  if (yuv_metadata.image())
-    *backend_texture = yuv_metadata.image()->getBackendTexture(true);
-  DLOG_IF(ERROR, !backend_texture->isValid())
-      << "Failed to full fill the promise texture from yuv resources.";
-}
-
-void SkiaOutputSurfaceImpl::OnPromiseTextureFullfill(
-    const RenderPassId id,
-    GrBackendTexture* backend_texture) {
-  auto it = offscreen_surfaces_.find(id);
-  DCHECK(it != offscreen_surfaces_.end());
-  sk_sp<SkSurface>& surface = it->second;
-  *backend_texture =
-      surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess);
-  DLOG_IF(ERROR, !backend_texture->isValid())
-      << "Failed to full fill the promise texture created from RenderPassId:"
-      << id;
-}
-
-void SkiaOutputSurfaceImpl::OnSwapBuffers() {
-  uint64_t swap_id = swap_id_++;
-  pending_swap_completed_ids_.push_back(swap_id);
-
-  // Only push to |pending_presented_ids_| if presentation callbacks
-  // are enabled, otherwise these will never be popped.
-  if (gl::IsPresentationCallbackEnabled())
-    pending_presented_ids_.push_back(swap_id);
-}
-
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 5cae56f..fb17b7f 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -11,39 +11,33 @@
 #include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/in_process_command_buffer.h"
-#include "gpu/ipc/service/image_transport_surface_delegate.h"
 #include "third_party/skia/include/core/SkDeferredDisplayListRecorder.h"
-#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkSurfaceCharacterization.h"
 
 namespace base {
 class WaitableEvent;
 }
 
-namespace gl {
-class GLSurface;
-}
-
-namespace gpu {
-class SyncPointClientState;
-class TextureBase;
-}
-
 namespace viz {
 
 class GpuServiceImpl;
 class VizProcessContextProvider;
+class SkiaOutputSurfaceImplOnGpu;
 class SyntheticBeginFrameSource;
 
+class YUVResourceMetadata;
+
 // The SkiaOutputSurface implementation. It is the output surface for
 // SkiaRenderer. It lives on the compositor thread, but it will post tasks
-// to the GPU thread for initializing, reshaping and swapping buffers, etc.
-// Currently, SkiaOutputSurfaceImpl sets up SkSurface from the default GL
-// framebuffer, creates SkDeferredDisplayListRecorder and SkCanvas for
-// SkiaRenderer to render into. In SwapBuffers, it detaches a
-// SkDeferredDisplayList from the recorder and plays it back on the framebuffer
-// SkSurface on the GPU thread.
-class SkiaOutputSurfaceImpl : public SkiaOutputSurface,
-                              public gpu::ImageTransportSurfaceDelegate {
+// to the GPU thread for initializing. Currently, SkiaOutputSurfaceImpl
+// create a SkiaOutputSurfaceImplOnGpu on the GPU thread. It will be used
+// for creating a SkSurface from the default framebuffer and providing the
+// SkSurfaceCharacterization for the SkSurface. And then SkiaOutputSurfaceImpl
+// will create SkDeferredDisplayListRecorder and SkCanvas for SkiaRenderer to
+// render into. In SwapBuffers, it detaches a SkDeferredDisplayList from the
+// recorder and plays it back on the framebuffer SkSurface on the GPU thread
+// through SkiaOutputSurfaceImpleOnGpu.
+class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
  public:
   SkiaOutputSurfaceImpl(
       GpuServiceImpl* gpu_service,
@@ -93,80 +87,23 @@
                                                   bool mipmap) override;
   void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
 
-  // gpu::ImageTransportSurfaceDelegate implementation:
-  void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params) override;
-  const gpu::gles2::FeatureInfo* GetFeatureInfo() const override;
-  const gpu::GpuPreferences& GetGpuPreferences() const override;
-
-  void SetSnapshotRequestedCallback(const base::Closure& callback) override;
-  void UpdateVSyncParameters(base::TimeTicks timebase,
-                             base::TimeDelta interval) override;
-  void BufferPresented(const gfx::PresentationFeedback& feedback) override;
-
-  void AddFilter(IPC::MessageFilter* message_filter) override;
-  int32_t GetRouteID() const override;
-
  private:
-  class YUVResourceMetadata;
-  void InitializeOnGpuThread(base::WaitableEvent* event);
-  void DestroyOnGpuThread(base::WaitableEvent* event);
-  void ReshapeOnGpuThread(const gfx::Size& size,
-                          float device_scale_factor,
-                          const gfx::ColorSpace& color_space,
-                          bool has_alpha,
-                          bool use_stencil,
-                          SkSurfaceCharacterization* characterization,
-                          base::WaitableEvent* event);
-  void SwapBuffersOnGpuThread(
-      OutputSurfaceFrame frame,
-      std::unique_ptr<SkDeferredDisplayList> ddl,
-      std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
-      uint64_t sync_fence_release);
-  void FinishPaintRenderPassOnGpuThread(
-      RenderPassId id,
-      std::unique_ptr<SkDeferredDisplayList> ddl,
-      std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
-      uint64_t sync_fence_release);
-  void RemoveRenderPassResourceOnGpuThread(std::vector<RenderPassId> ids);
-  void RecreateRecorder();
-  void PreprocessYUVResources(
-      std::vector<YUVResourceMetadata*> yuv_resource_metadatas);
-  void BindOrCopyTextureIfNecessary(gpu::TextureBase* texture_base);
-  void DidSwapBuffersCompleteOnClientThread(
-      gpu::SwapBuffersCompleteParams params);
-  void UpdateVSyncParametersOnClientThread(base::TimeTicks timebase,
-                                           base::TimeDelta interval);
-  void BufferPresentedOnClientThread(uint64_t swap_id,
-                                     const gfx::PresentationFeedback& feedback);
-
   template <class T>
   class PromiseTextureHelper;
-  // Fullfill callback for promise SkImage created from a resource.
-  void OnPromiseTextureFullfill(const ResourceMetadata& metadata,
-                                GrBackendTexture* backend_texture);
-  // Fullfill callback for promise SkImage created from YUV resources.
-  void OnPromiseTextureFullfill(const YUVResourceMetadata& metadata,
-                                GrBackendTexture* backend_texture);
-  // Fullfill callback for promise SkImage created from a render pass.
-  void OnPromiseTextureFullfill(const RenderPassId id,
-                                GrBackendTexture* backend_texture);
+  void InitializeOnGpuThread(base::WaitableEvent* event);
+  void RecreateRecorder();
+  void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params);
+  void UpdateVSyncParameters(base::TimeTicks timebase,
+                             base::TimeDelta interval);
+  void BufferPresented(uint64_t swap_id,
+                       const gfx::PresentationFeedback& feedback);
 
-  // Generage the next swap ID and push it to our pending swap ID queues.
-  void OnSwapBuffers();
-
-  const gpu::CommandBufferId command_buffer_id_;
   uint64_t sync_fence_release_ = 0;
   GpuServiceImpl* const gpu_service_;
   const gpu::SurfaceHandle surface_handle_;
   SyntheticBeginFrameSource* const synthetic_begin_frame_source_;
   OutputSurfaceClient* client_ = nullptr;
 
-  scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
-
-  gpu::GpuPreferences gpu_preferences_;
-  scoped_refptr<gl::GLSurface> surface_;
-
-  sk_sp<SkSurface> sk_surface_;
   SkSurfaceCharacterization characterization_;
   std::unique_ptr<SkDeferredDisplayListRecorder> recorder_;
 
@@ -178,10 +115,6 @@
   // the GPU thread.
   std::unique_ptr<SkDeferredDisplayListRecorder> offscreen_surface_recorder_;
 
-  // Offscreen surfaces for render passes. It must be accessed on the GPU
-  // thread.
-  base::flat_map<RenderPassId, sk_sp<SkSurface>> offscreen_surfaces_;
-
   // Sync tokens for resources which are used for the current frame.
   std::vector<gpu::SyncToken> resource_sync_tokens_;
 
@@ -191,21 +124,16 @@
   // directly.
   std::vector<YUVResourceMetadata*> yuv_resource_metadatas_;
 
-  // ID is pushed each time we begin a swap, and popped each time we present or
-  // complete a swap.
-  base::circular_deque<uint64_t> pending_presented_ids_;
-  base::circular_deque<uint64_t> pending_swap_completed_ids_;
-  uint64_t swap_id_ = 0;
-
   // The task runner for running task on the client (compositor) thread.
   scoped_refptr<base::SingleThreadTaskRunner> client_thread_task_runner_;
 
-  THREAD_CHECKER(client_thread_checker_);
-  THREAD_CHECKER(gpu_thread_checker_);
+  // |impl_on_gpu| is created and destroyed on the GPU thread.
+  std::unique_ptr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_;
 
-  base::WeakPtr<SkiaOutputSurfaceImpl> client_thread_weak_ptr_;
-  base::WeakPtrFactory<SkiaOutputSurfaceImpl> gpu_thread_weak_ptr_factory_;
-  base::WeakPtrFactory<SkiaOutputSurfaceImpl> client_thread_weak_ptr_factory_;
+  THREAD_CHECKER(thread_checker_);
+
+  base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr_;
+  base::WeakPtrFactory<SkiaOutputSurfaceImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurfaceImpl);
 };
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
new file mode 100644
index 0000000..60296cdd
--- /dev/null
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -0,0 +1,426 @@
+// 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 "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/callback_helpers.h"
+#include "base/synchronization/waitable_event.h"
+#include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/gl/gpu_service_impl.h"
+#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
+#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/scheduler.h"
+#include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/service/texture_base.h"
+#include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/ipc/service/image_transport_surface.h"
+#include "third_party/skia/include/private/SkDeferredDisplayList.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_switches_util.h"
+#include "ui/gl/gl_version_info.h"
+
+namespace viz {
+namespace {
+
+base::AtomicSequenceNumber g_next_command_buffer_id;
+
+}  // namespace
+
+YUVResourceMetadata::YUVResourceMetadata(
+    std::vector<ResourceMetadata> metadatas,
+    SkYUVColorSpace yuv_color_space)
+    : metadatas_(std::move(metadatas)), yuv_color_space_(yuv_color_space) {
+  DCHECK(metadatas_.size() == 2 || metadatas_.size() == 3);
+}
+
+YUVResourceMetadata::YUVResourceMetadata(YUVResourceMetadata&& other) = default;
+
+YUVResourceMetadata::~YUVResourceMetadata() = default;
+
+YUVResourceMetadata& YUVResourceMetadata::operator=(
+    YUVResourceMetadata&& other) = default;
+
+SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu(
+    GpuServiceImpl* gpu_service,
+    gpu::SurfaceHandle surface_handle,
+    const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
+    const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
+    const BufferPresentedCallback& buffer_presented_callback)
+    : command_buffer_id_(gpu::CommandBufferId::FromUnsafeValue(
+          g_next_command_buffer_id.GetNext() + 1)),
+      gpu_service_(gpu_service),
+      surface_handle_(surface_handle),
+      did_swap_buffer_complete_callback_(did_swap_buffer_complete_callback),
+      update_vsync_parameters_callback_(update_vsync_parameters_callback),
+      buffer_presented_callback_(buffer_presented_callback),
+      weak_ptr_factory_(this) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
+
+  sync_point_client_state_ =
+      gpu_service_->sync_point_manager()->CreateSyncPointClientState(
+          gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_,
+          gpu_service_->skia_output_surface_sequence_id());
+
+  surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
+      weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat());
+  DCHECK(surface_);
+
+  if (!gpu_service_->CreateGrContextIfNecessary(surface_.get())) {
+    LOG(FATAL) << "Failed to create GrContext";
+    // TODO(penghuang): handle the failure.
+  }
+
+  DCHECK(gpu_service_->context_for_skia());
+  DCHECK(gpu_service_->gr_context());
+
+  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle the failure.
+  }
+
+  capabilities_.flipped_output_surface = surface_->FlipsVertically();
+
+  // Get stencil bits from the default frame buffer.
+  auto* current_gl = gpu_service_->context_for_skia()->GetCurrentGL();
+  const auto* version = current_gl->Version;
+  auto* api = current_gl->Api;
+  GLint stencil_bits = 0;
+  if (version->is_desktop_core_profile) {
+    api->glGetFramebufferAttachmentParameterivEXTFn(
+        GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
+        &stencil_bits);
+  } else {
+    api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
+  }
+
+  capabilities_.supports_stencil = stencil_bits > 0;
+}
+
+SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // Destroy the surface with the context current, some surface destructors
+  // make GL calls.
+  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle the failure.
+  }
+  surface_ = nullptr;
+  sync_point_client_state_ = nullptr;
+}
+
+void SkiaOutputSurfaceImplOnGpu::Reshape(
+    const gfx::Size& size,
+    float device_scale_factor,
+    const gfx::ColorSpace& color_space,
+    bool has_alpha,
+    bool use_stencil,
+    SkSurfaceCharacterization* characterization,
+    base::WaitableEvent* event) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  std::unique_ptr<base::ScopedClosureRunner> scoped_runner;
+  if (event) {
+    scoped_runner = std::make_unique<base::ScopedClosureRunner>(
+        base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
+  }
+
+  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle the failure.
+  }
+  gl::GLSurface::ColorSpace surface_color_space =
+      color_space == gfx::ColorSpace::CreateSCRGBLinear()
+          ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
+          : gl::GLSurface::ColorSpace::UNSPECIFIED;
+  if (!surface_->Resize(size, device_scale_factor, surface_color_space,
+                        has_alpha)) {
+    LOG(FATAL) << "Failed to resize.";
+    // TODO(penghuang): Handle the failure.
+  }
+  DCHECK(gpu_service_->context_for_skia()->IsCurrent(surface_.get()));
+  DCHECK(gpu_service_->gr_context());
+
+  SkSurfaceProps surface_props =
+      SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+
+  GrGLFramebufferInfo framebuffer_info;
+  framebuffer_info.fFBOID = 0;
+  const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
+  framebuffer_info.fFormat = version_info->is_es ? GL_BGRA8_EXT : GL_RGBA8;
+
+  GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
+                                      framebuffer_info);
+
+  sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
+      gpu_service_->gr_context(), render_target, kBottomLeft_GrSurfaceOrigin,
+      kBGRA_8888_SkColorType, nullptr, &surface_props);
+  DCHECK(sk_surface_);
+
+  if (characterization) {
+    sk_surface_->characterize(characterization);
+    DCHECK(characterization->isValid());
+  }
+}
+
+void SkiaOutputSurfaceImplOnGpu::SwapBuffers(
+    OutputSurfaceFrame frame,
+    std::unique_ptr<SkDeferredDisplayList> ddl,
+    std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
+    uint64_t sync_fence_release) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(ddl);
+  DCHECK(sk_surface_);
+
+  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle the failure.
+  }
+
+  PreprocessYUVResources(std::move(yuv_resource_metadatas));
+
+  sk_surface_->draw(ddl.get());
+  gpu_service_->gr_context()->flush();
+  OnSwapBuffers();
+  surface_->SwapBuffers(
+      base::BindRepeating([](const gfx::PresentationFeedback&) {}));
+  sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
+}
+
+void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
+    RenderPassId id,
+    std::unique_ptr<SkDeferredDisplayList> ddl,
+    std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
+    uint64_t sync_fence_release) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(ddl);
+
+  if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle resize failure.
+  }
+
+  PreprocessYUVResources(std::move(yuv_resource_metadatas));
+
+  auto& surface = offscreen_surfaces_[id];
+  SkSurfaceCharacterization characterization;
+  // TODO(penghuang): Using characterization != ddl->characterization(), when
+  // the SkSurfaceCharacterization::operator!= is implemented in Skia.
+  if (!surface || !surface->characterize(&characterization) ||
+      characterization != ddl->characterization()) {
+    surface = SkSurface::MakeRenderTarget(
+        gpu_service_->gr_context(), ddl->characterization(), SkBudgeted::kNo);
+    DCHECK(surface);
+  }
+  surface->draw(ddl.get());
+  surface->flush();
+  sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
+}
+
+void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource(
+    std::vector<RenderPassId> ids) {
+  DCHECK(!ids.empty());
+  for (const auto& id : ids) {
+    auto it = offscreen_surfaces_.find(id);
+    DCHECK(it != offscreen_surfaces_.end());
+    offscreen_surfaces_.erase(it);
+  }
+}
+
+void SkiaOutputSurfaceImplOnGpu::FullfillPromiseTexture(
+    const ResourceMetadata& metadata,
+    GrBackendTexture* backend_texture) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto* mailbox_manager = gpu_service_->mailbox_manager();
+  auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
+  if (!texture_base) {
+    DLOG(ERROR) << "Failed to full fill the promise texture.";
+    return;
+  }
+  BindOrCopyTextureIfNecessary(texture_base);
+  GrGLTextureInfo texture_info;
+  texture_info.fTarget = texture_base->target();
+  texture_info.fID = texture_base->service_id();
+  texture_info.fFormat = *metadata.backend_format.getGLFormat();
+  *backend_texture =
+      GrBackendTexture(metadata.size.width(), metadata.size.height(),
+                       metadata.mip_mapped, texture_info);
+}
+
+void SkiaOutputSurfaceImplOnGpu::FullfillPromiseTexture(
+    const YUVResourceMetadata& yuv_metadata,
+    GrBackendTexture* backend_texture) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (yuv_metadata.image())
+    *backend_texture = yuv_metadata.image()->getBackendTexture(true);
+  DLOG_IF(ERROR, !backend_texture->isValid())
+      << "Failed to full fill the promise texture from yuv resources.";
+}
+
+void SkiaOutputSurfaceImplOnGpu::FullfillPromiseTexture(
+    const RenderPassId id,
+    GrBackendTexture* backend_texture) {
+  auto it = offscreen_surfaces_.find(id);
+  DCHECK(it != offscreen_surfaces_.end());
+  sk_sp<SkSurface>& surface = it->second;
+  *backend_texture =
+      surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess);
+  DLOG_IF(ERROR, !backend_texture->isValid())
+      << "Failed to full fill the promise texture created from RenderPassId:"
+      << id;
+}
+
+#if defined(OS_WIN)
+void SkiaOutputSurfaceImplOnGpu::DidCreateAcceleratedSurfaceChildWindow(
+    gpu::SurfaceHandle parent_window,
+    gpu::SurfaceHandle child_window) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+}
+#endif
+
+void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete(
+    gpu::SwapBuffersCompleteParams params) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  params.swap_response.swap_id = pending_swap_completed_ids_.front();
+  pending_swap_completed_ids_.pop_front();
+  did_swap_buffer_complete_callback_.Run(params);
+}
+
+const gpu::gles2::FeatureInfo* SkiaOutputSurfaceImplOnGpu::GetFeatureInfo()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+const gpu::GpuPreferences& SkiaOutputSurfaceImplOnGpu::GetGpuPreferences()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+  return gpu_preferences_;
+}
+
+void SkiaOutputSurfaceImplOnGpu::SetSnapshotRequestedCallback(
+    const base::Closure& callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+}
+
+void SkiaOutputSurfaceImplOnGpu::UpdateVSyncParameters(
+    base::TimeTicks timebase,
+    base::TimeDelta interval) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(!gl::IsPresentationCallbackEnabled());
+  update_vsync_parameters_callback_.Run(timebase, interval);
+}
+
+void SkiaOutputSurfaceImplOnGpu::BufferPresented(
+    const gfx::PresentationFeedback& feedback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(gl::IsPresentationCallbackEnabled());
+  uint64_t swap_id = pending_presented_ids_.front();
+  pending_presented_ids_.pop_front();
+  buffer_presented_callback_.Run(swap_id, feedback);
+}
+
+void SkiaOutputSurfaceImplOnGpu::AddFilter(IPC::MessageFilter* message_filter) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+}
+
+int32_t SkiaOutputSurfaceImplOnGpu::GetRouteID() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary(
+    gpu::TextureBase* texture_base) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (gpu_service_->gpu_preferences().use_passthrough_cmd_decoder)
+    return;
+  // If a texture created with non-passthrough command buffer and bind with
+  // an image, the Chrome will defer copying the image to the texture until
+  // the texture is used. It is for implementing low latency drawing and
+  // avoiding unnecessary texture copy. So we need check the texture image
+  // state, and bind or copy the image to the texture if necessary.
+  auto* texture = static_cast<gpu::gles2::Texture*>(texture_base);
+  gpu::gles2::Texture::ImageState image_state;
+  auto* image = texture->GetLevelImage(GL_TEXTURE_2D, 0, &image_state);
+  if (image && image_state == gpu::gles2::Texture::UNBOUND) {
+    glBindTexture(texture_base->target(), texture_base->service_id());
+    if (image->BindTexImage(texture_base->target())) {
+    } else {
+      texture->SetLevelImageState(texture_base->target(), 0,
+                                  gpu::gles2::Texture::COPIED);
+      if (!image->CopyTexImage(texture_base->target()))
+        LOG(ERROR) << "Failed to copy a gl image to texture.";
+    }
+  }
+}
+
+void SkiaOutputSurfaceImplOnGpu::PreprocessYUVResources(
+    std::vector<YUVResourceMetadata*> yuv_resource_metadatas) {
+  // Create SkImage for fullfilling YUV promise image, before drawing the ddl.
+  // TODO(penghuang): Remove the extra step when Skia supports drawing YUV
+  // textures directly.
+  auto* mailbox_manager = gpu_service_->mailbox_manager();
+  for (auto* yuv_metadata : yuv_resource_metadatas) {
+    const auto& metadatas = yuv_metadata->metadatas();
+    DCHECK(metadatas.size() == 2 || metadatas.size() == 3);
+    GrBackendTexture backend_textures[3];
+    size_t i = 0;
+    for (const auto& metadata : metadatas) {
+      auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
+      if (!texture_base)
+        break;
+      BindOrCopyTextureIfNecessary(texture_base);
+      GrGLTextureInfo texture_info;
+      texture_info.fTarget = texture_base->target();
+      texture_info.fID = texture_base->service_id();
+      texture_info.fFormat = *metadata.backend_format.getGLFormat();
+      backend_textures[i++] =
+          GrBackendTexture(metadata.size.width(), metadata.size.height(),
+                           GrMipMapped::kNo, texture_info);
+    }
+
+    if (i != metadatas.size())
+      continue;
+
+    sk_sp<SkImage> image;
+    if (metadatas.size() == 2) {
+      image = SkImage::MakeFromNV12TexturesCopy(
+          gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
+          backend_textures, kTopLeft_GrSurfaceOrigin,
+          nullptr /* image_color_space */);
+      DCHECK(image);
+    } else {
+      image = SkImage::MakeFromYUVTexturesCopy(
+          gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
+          backend_textures, kTopLeft_GrSurfaceOrigin,
+          nullptr /* image_color_space */);
+      DCHECK(image);
+    }
+    yuv_metadata->set_image(std::move(image));
+  }
+}
+
+void SkiaOutputSurfaceImplOnGpu::OnSwapBuffers() {
+  uint64_t swap_id = swap_id_++;
+  pending_swap_completed_ids_.push_back(swap_id);
+
+  // Only push to |pending_presented_ids_| if presentation callbacks
+  // are enabled, otherwise these will never be popped.
+  if (gl::IsPresentationCallbackEnabled())
+    pending_presented_ids_.push_back(swap_id);
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
new file mode 100644
index 0000000..fea55b8
--- /dev/null
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -0,0 +1,181 @@
+// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_
+
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "build/build_config.h"
+#include "components/viz/common/quads/render_pass.h"
+#include "components/viz/common/resources/resource_metadata.h"
+#include "components/viz/service/display/output_surface.h"
+#include "components/viz/service/display/output_surface_frame.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "gpu/ipc/in_process_command_buffer.h"
+#include "gpu/ipc/service/image_transport_surface_delegate.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+class SkDeferredDisplayList;
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace gl {
+class GLSurface;
+}
+
+namespace gpu {
+class SyncPointClientState;
+}
+
+namespace viz {
+
+class GpuServiceImpl;
+
+// Metadata for YUV promise SkImage.
+class YUVResourceMetadata {
+ public:
+  YUVResourceMetadata(std::vector<ResourceMetadata> metadatas,
+                      SkYUVColorSpace yuv_color_space);
+  YUVResourceMetadata(YUVResourceMetadata&& other);
+  ~YUVResourceMetadata();
+  YUVResourceMetadata& operator=(YUVResourceMetadata&& other);
+
+  const std::vector<ResourceMetadata>& metadatas() const { return metadatas_; }
+  SkYUVColorSpace yuv_color_space() const { return yuv_color_space_; }
+  const sk_sp<SkImage> image() const { return image_; }
+  void set_image(sk_sp<SkImage> image) { image_ = image; }
+  const gfx::Size size() const { return metadatas_[0].size; }
+
+ private:
+  // Metadatas for YUV planes.
+  std::vector<ResourceMetadata> metadatas_;
+
+  SkYUVColorSpace yuv_color_space_;
+
+  // The image copied from YUV textures, it is for fullfilling the promise
+  // image.
+  // TODO(penghuang): Remove it when Skia supports drawing YUV textures
+  // directly.
+  sk_sp<SkImage> image_;
+
+  DISALLOW_COPY_AND_ASSIGN(YUVResourceMetadata);
+};
+
+// The SkiaOutputSurface implementation running on the GPU thread. This class
+// should be created, used and destroyed on the GPU thread.
+class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
+ public:
+  using DidSwapBufferCompleteCallback =
+      base::RepeatingCallback<void(gpu::SwapBuffersCompleteParams)>;
+  using UpdateVSyncParametersCallback =
+      base::RepeatingCallback<void(base::TimeTicks, base::TimeDelta)>;
+  using BufferPresentedCallback =
+      base::RepeatingCallback<void(uint64_t swap_id,
+                                   const gfx::PresentationFeedback& feedback)>;
+  SkiaOutputSurfaceImplOnGpu(
+      GpuServiceImpl* gpu_service,
+      gpu::SurfaceHandle surface_handle,
+      const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
+      const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
+      const BufferPresentedCallback& buffer_presented_callback);
+  ~SkiaOutputSurfaceImplOnGpu() override;
+
+  gpu::CommandBufferId command_buffer_id() const { return command_buffer_id_; }
+  const OutputSurface::Capabilities capabilities() const {
+    return capabilities_;
+  }
+  const base::WeakPtr<SkiaOutputSurfaceImplOnGpu>& weak_ptr() const {
+    return weak_ptr_;
+  }
+
+  void Reshape(const gfx::Size& size,
+               float device_scale_factor,
+               const gfx::ColorSpace& color_space,
+               bool has_alpha,
+               bool use_stencil,
+               SkSurfaceCharacterization* characterization,
+               base::WaitableEvent* event);
+  void SwapBuffers(OutputSurfaceFrame frame,
+                   std::unique_ptr<SkDeferredDisplayList> ddl,
+                   std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
+                   uint64_t sync_fence_release);
+  void FinishPaintRenderPass(
+      RenderPassId id,
+      std::unique_ptr<SkDeferredDisplayList> ddl,
+      std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
+      uint64_t sync_fence_release);
+  void RemoveRenderPassResource(std::vector<RenderPassId> ids);
+
+  // Fullfill callback for promise SkImage created from a resource.
+  void FullfillPromiseTexture(const ResourceMetadata& metadata,
+                              GrBackendTexture* backend_texture);
+  // Fullfill callback for promise SkImage created from YUV resources.
+  void FullfillPromiseTexture(const YUVResourceMetadata& metadata,
+                              GrBackendTexture* backend_texture);
+  // Fullfill callback for promise SkImage created from a render pass.
+  void FullfillPromiseTexture(const RenderPassId id,
+                              GrBackendTexture* backend_texture);
+
+ private:
+// gpu::ImageTransportSurfaceDelegate implementation:
+#if defined(OS_WIN)
+  void DidCreateAcceleratedSurfaceChildWindow(
+      gpu::SurfaceHandle parent_window,
+      gpu::SurfaceHandle child_window) override;
+#endif
+  void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params) override;
+  const gpu::gles2::FeatureInfo* GetFeatureInfo() const override;
+  const gpu::GpuPreferences& GetGpuPreferences() const override;
+  void SetSnapshotRequestedCallback(const base::Closure& callback) override;
+  void UpdateVSyncParameters(base::TimeTicks timebase,
+                             base::TimeDelta interval) override;
+  void BufferPresented(const gfx::PresentationFeedback& feedback) override;
+  void AddFilter(IPC::MessageFilter* message_filter) override;
+  int32_t GetRouteID() const override;
+
+  void BindOrCopyTextureIfNecessary(gpu::TextureBase* texture_base);
+  void PreprocessYUVResources(
+      std::vector<YUVResourceMetadata*> yuv_resource_metadatas);
+
+  // Generage the next swap ID and push it to our pending swap ID queues.
+  void OnSwapBuffers();
+
+  const gpu::CommandBufferId command_buffer_id_;
+  GpuServiceImpl* const gpu_service_;
+  const gpu::SurfaceHandle surface_handle_;
+  DidSwapBufferCompleteCallback did_swap_buffer_complete_callback_;
+  UpdateVSyncParametersCallback update_vsync_parameters_callback_;
+  BufferPresentedCallback buffer_presented_callback_;
+  scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
+  gpu::GpuPreferences gpu_preferences_;
+  scoped_refptr<gl::GLSurface> surface_;
+  sk_sp<SkSurface> sk_surface_;
+  OutputSurface::Capabilities capabilities_;
+
+  // Offscreen surfaces for render passes. It can only be accessed on GPU
+  // thread.
+  base::flat_map<RenderPassId, sk_sp<SkSurface>> offscreen_surfaces_;
+
+  // ID is pushed each time we begin a swap, and popped each time we present or
+  // complete a swap.
+  base::circular_deque<uint64_t> pending_presented_ids_;
+  base::circular_deque<uint64_t> pending_swap_completed_ids_;
+  uint64_t swap_id_ = 0;
+
+  THREAD_CHECKER(thread_checker_);
+
+  base::WeakPtr<SkiaOutputSurfaceImplOnGpu> weak_ptr_;
+  base::WeakPtrFactory<SkiaOutputSurfaceImplOnGpu> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurfaceImplOnGpu);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_ON_GPU_H_
diff --git a/components/zucchini/BUILD.gn b/components/zucchini/BUILD.gn
index 800216e..f36c949 100644
--- a/components/zucchini/BUILD.gn
+++ b/components/zucchini/BUILD.gn
@@ -2,9 +2,22 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
 import("//chrome/process_version_rc_template.gni")
 import("//testing/test.gni")
 
+buildflag_header("buildflags") {
+  header = "buildflags.h"
+
+  # Disable DEX on Windows Official Builds.
+  _enable_dex = !(is_win && is_official_build)
+  _enable_win = true
+  flags = [
+    "ENABLE_DEX=$_enable_dex",
+    "ENABLE_WIN=$_enable_win",
+  ]
+}
+
 static_library("zucchini_lib") {
   sources = [
     "abs32_utils.cc",
@@ -79,6 +92,7 @@
   ]
 
   deps = [
+    ":buildflags",
     "//base",
   ]
 }
diff --git a/components/zucchini/element_detection.cc b/components/zucchini/element_detection.cc
index 2fa3604..a826f5423 100644
--- a/components/zucchini/element_detection.cc
+++ b/components/zucchini/element_detection.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "components/zucchini/buildflags.h"
 #include "components/zucchini/disassembler.h"
 #include "components/zucchini/disassembler_dex.h"
 #include "components/zucchini/disassembler_no_op.h"
@@ -17,7 +18,7 @@
 namespace {
 
 // Impose a minimal program size to eliminate pathological cases.
-constexpr size_t kMinProgramSize = 16;
+enum : size_t { kMinProgramSize = 16 };
 
 }  // namespace
 
@@ -25,6 +26,7 @@
 
 std::unique_ptr<Disassembler> MakeDisassemblerWithoutFallback(
     ConstBufferView image) {
+#if BUILDFLAG(ENABLE_WIN)
   if (DisassemblerWin32X86::QuickDetect(image)) {
     auto disasm = Disassembler::Make<DisassemblerWin32X86>(image);
     if (disasm && disasm->size() >= kMinProgramSize)
@@ -36,12 +38,15 @@
     if (disasm && disasm->size() >= kMinProgramSize)
       return disasm;
   }
+#endif  // BUILDFLAG(ENABLE_WIN)
 
+#if BUILDFLAG(ENABLE_DEX)
   if (DisassemblerDex::QuickDetect(image)) {
     auto disasm = Disassembler::Make<DisassemblerDex>(image);
     if (disasm && disasm->size() >= kMinProgramSize)
       return disasm;
   }
+#endif  // BUILDFLAG(ENABLE_DEX)
 
   return nullptr;
 }
@@ -49,15 +54,20 @@
 std::unique_ptr<Disassembler> MakeDisassemblerOfType(ConstBufferView image,
                                                      ExecutableType exe_type) {
   switch (exe_type) {
+#if BUILDFLAG(ENABLE_WIN)
     case kExeTypeWin32X86:
       return Disassembler::Make<DisassemblerWin32X86>(image);
     case kExeTypeWin32X64:
       return Disassembler::Make<DisassemblerWin32X64>(image);
+#endif  // BUILDFLAG(ENABLE_WIN)
+#if BUILDFLAG(ENABLE_DEX)
     case kExeTypeDex:
       return Disassembler::Make<DisassemblerDex>(image);
+#endif  // BUILDFLAG(ENABLE_DEX)
     case kExeTypeNoOp:
       return Disassembler::Make<DisassemblerNoOp>(image);
     default:
+      // If an architecture is disabled then null is handled gracefully.
       return nullptr;
   }
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 7c00f50..17e450a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2056,6 +2056,8 @@
       "android/gesture_listener_manager.h",
       "android/ime_adapter_android.cc",
       "android/ime_adapter_android.h",
+      "android/interstitial_page_delegate_android.cc",
+      "android/interstitial_page_delegate_android.h",
       "android/java/gin_java_bound_object.cc",
       "android/java/gin_java_bound_object.h",
       "android/java/gin_java_bound_object_delegate.cc",
@@ -2148,10 +2150,11 @@
     deps += [
       ":reflection_jni_headers",
       "//content/public/android:jni",
-      "//device/gamepad/public/mojom",
       "//media",
       "//media/capture/content/android",
       "//media/capture/video/android",
+      "//ui/accessibility:ax_assistant",
+      "//ui/accessibility/mojom",
       "//ui/android",
       "//ui/compositor",
     ]
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 89e57e0e..041eac9 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -16,9 +16,9 @@
 #include "content/common/accessibility_messages.h"
 #include "content/public/common/content_client.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/accessibility/ax_assistant_structure.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
-#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
 
 namespace {
@@ -265,7 +265,7 @@
 }
 
 bool BrowserAccessibilityAndroid::IsLink() const {
-  return ui::AXSnapshotNodeAndroid::AXRoleIsLink(GetRole());
+  return ui::AXRoleIsLink(GetRole());
 }
 
 bool BrowserAccessibilityAndroid::IsMultiLine() const {
@@ -370,8 +370,8 @@
 }
 
 const char* BrowserAccessibilityAndroid::GetClassName() const {
-  return ui::AXSnapshotNodeAndroid::AXRoleToAndroidClassName(
-      GetRole(), PlatformGetParent() != nullptr);
+  return ui::AXRoleToAndroidClassName(GetRole(),
+                                      PlatformGetParent() != nullptr);
 }
 
 base::string16 BrowserAccessibilityAndroid::GetText() const {
@@ -421,7 +421,7 @@
   if (text.empty() && (IsLink() || GetRole() == ax::mojom::Role::kImage) &&
       !HasExplicitlyEmptyName()) {
     base::string16 url = GetString16Attribute(ax::mojom::StringAttribute::kUrl);
-    text = ui::AXSnapshotNodeAndroid::AXUrlBaseText(url);
+    text = ui::AXUrlBaseText(url);
   }
 
   return text;
diff --git a/content/public/test/android/interstitial_page_delegate_android.cc b/content/browser/android/interstitial_page_delegate_android.cc
similarity index 76%
rename from content/public/test/android/interstitial_page_delegate_android.cc
rename to content/browser/android/interstitial_page_delegate_android.cc
index d59c07d..81b57c0f 100644
--- a/content/public/test/android/interstitial_page_delegate_android.cc
+++ b/content/browser/android/interstitial_page_delegate_android.cc
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/public/test/android/interstitial_page_delegate_android.h"
+#include "content/browser/android/interstitial_page_delegate_android.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "content/public/browser/interstitial_page.h"
-#include "content/public/browser/web_contents.h"
 #include "jni/InterstitialPageDelegateAndroid_jni.h"
 
 using base::android::AttachCurrentThread;
@@ -21,7 +20,10 @@
     JNIEnv* env,
     jobject obj,
     const std::string& html_content)
-    : weak_java_obj_(env, obj), html_content_(html_content), page_(NULL) {}
+    : weak_java_obj_(env, obj),
+      html_content_(html_content),
+      page_(NULL) {
+}
 
 InterstitialPageDelegateAndroid::~InterstitialPageDelegateAndroid() {
   JNIEnv* env = AttachCurrentThread();
@@ -44,20 +46,6 @@
     page_->DontProceed();
 }
 
-void InterstitialPageDelegateAndroid::ShowInterstitialPage(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    const JavaParamRef<jstring>& jurl,
-    const JavaParamRef<jobject>& jweb_contents) {
-  WebContents* web_contents = WebContents::FromJavaWebContents(jweb_contents);
-  DCHECK(web_contents);
-  GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
-  InterstitialPage* interstitial =
-      InterstitialPage::Create(web_contents, false, url, this);
-  set_interstitial_page(interstitial);
-  interstitial->Show();
-}
-
 std::string InterstitialPageDelegateAndroid::GetHTMLContents() {
   return html_content_;
 }
@@ -84,8 +72,8 @@
     std::string sanitized_command(command);
     // The JSONified response has quotes, remove them.
     if (sanitized_command.length() > 1 && sanitized_command[0] == '"') {
-      sanitized_command =
-          sanitized_command.substr(1, sanitized_command.length() - 2);
+      sanitized_command = sanitized_command.substr(
+          1, sanitized_command.length() - 2);
     }
 
     Java_InterstitialPageDelegateAndroid_commandReceived(
diff --git a/content/public/test/android/interstitial_page_delegate_android.h b/content/browser/android/interstitial_page_delegate_android.h
similarity index 77%
rename from content/public/test/android/interstitial_page_delegate_android.h
rename to content/browser/android/interstitial_page_delegate_android.h
index a2ef9f0..b4900e2 100644
--- a/content/public/test/android/interstitial_page_delegate_android.h
+++ b/content/browser/android/interstitial_page_delegate_android.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_PUBLIC_TEST_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
-#define CONTENT_PUBLIC_TEST_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
+#ifndef CONTENT_BROWSER_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
+#define CONTENT_BROWSER_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
 
 #include <jni.h>
 #include <string>
@@ -11,6 +11,7 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "content/common/content_export.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 
 namespace content {
@@ -32,11 +33,6 @@
   void Proceed(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void DontProceed(JNIEnv* env,
                    const base::android::JavaParamRef<jobject>& obj);
-  void ShowInterstitialPage(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& jurl,
-      const base::android::JavaParamRef<jobject>& jweb_contents);
 
   // Implementation of InterstitialPageDelegate
   std::string GetHTMLContents() override;
@@ -55,4 +51,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_PUBLIC_TEST_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
+#endif  // CONTENT_BROWSER_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index e482a432..75021cf 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1151,8 +1151,8 @@
   // CompositingModeReporter.
   return;
 #else
-  if (features::IsMusEnabled()) {
-    // Mus == ChromeOS, which doesn't support software compositing, so no need
+  if (features::IsMashEnabled()) {
+    // Mash == ChromeOS, which doesn't support software compositing, so no need
     // to report compositing mode.
     return;
   }
@@ -1189,7 +1189,7 @@
   InitializeMojo();
 
 #if BUILDFLAG(ENABLE_MUS)
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kEnableSurfaceSynchronization);
   }
@@ -1460,12 +1460,13 @@
 
   // Env creates the compositor. Aura widgets need the compositor to be created
   // before they can be initialized by the browser.
-  env_ = aura::Env::CreateInstance(
-      features::IsMusEnabled() ? aura::Env::Mode::MUS : aura::Env::Mode::LOCAL);
+  env_ = aura::Env::CreateInstance(features::IsMashEnabled()
+                                       ? aura::Env::Mode::MUS
+                                       : aura::Env::Mode::LOCAL);
 #endif  // defined(USE_AURA)
 
 #if BUILDFLAG(ENABLE_MUS)
-  if (features::IsMusEnabled())
+  if (features::IsMashEnabled())
     image_cursors_set_ = std::make_unique<ui::ImageCursorsSet>();
 #endif
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index 751c113..04799d6 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -172,6 +172,7 @@
                   const base::UnguessableToken& frame_token,
                   int32_t process_id,
                   std::unique_ptr<CreateLoaderParameters> create_loader_params,
+                  bool is_download,
                   network::mojom::URLLoaderRequest loader_request,
                   network::mojom::URLLoaderClientPtr client,
                   network::mojom::URLLoaderFactoryPtr target_factory);
@@ -263,6 +264,7 @@
   InterceptionStage stage_;
 
   std::unique_ptr<CreateLoaderParameters> create_loader_params_;
+  const bool is_download_;
 
   mojo::Binding<network::mojom::URLLoaderClient> client_binding_;
   mojo::Binding<network::mojom::URLLoader> loader_binding_;
@@ -308,6 +310,7 @@
 
   void CreateJob(const base::UnguessableToken& frame_token,
                  int32_t process_id,
+                 bool is_download,
                  std::unique_ptr<CreateLoaderParameters> create_params,
                  network::mojom::URLLoaderRequest loader_request,
                  network::mojom::URLLoaderClientPtr client,
@@ -317,10 +320,10 @@
     static int last_id = 0;
 
     std::string id = base::StringPrintf("interception-job-%d", ++last_id);
-    InterceptionJob* job =
-        new InterceptionJob(this, id, frame_token, process_id,
-                            std::move(create_params), std::move(loader_request),
-                            std::move(client), std::move(target_factory));
+    InterceptionJob* job = new InterceptionJob(
+        this, id, frame_token, process_id, std::move(create_params),
+        is_download, std::move(loader_request), std::move(client),
+        std::move(target_factory));
     jobs_.emplace(std::move(id), job);
   }
 
@@ -400,6 +403,7 @@
   DevToolsURLLoaderFactoryProxy(
       const base::UnguessableToken& frame_token,
       int32_t process_id,
+      bool is_download,
       network::mojom::URLLoaderFactoryRequest loader_request,
       network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
       base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor);
@@ -424,6 +428,7 @@
 
   const base::UnguessableToken frame_token_;
   const int32_t process_id_;
+  const bool is_download_;
 
   network::mojom::URLLoaderFactoryPtr target_factory_;
   base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor_;
@@ -435,11 +440,13 @@
 DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy(
     const base::UnguessableToken& frame_token,
     int32_t process_id,
+    bool is_download,
     network::mojom::URLLoaderFactoryRequest loader_request,
     network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
     base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor)
     : frame_token_(frame_token),
       process_id_(process_id),
+      is_download_(is_download),
       interceptor_(std::move(interceptor)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
   BrowserThread::PostTask(
@@ -472,9 +479,9 @@
       routing_id, request_id, options, request, traffic_annotation);
   network::mojom::URLLoaderFactoryPtr factory_clone;
   target_factory_->Clone(MakeRequest(&factory_clone));
-  interceptor->CreateJob(frame_token_, process_id_, std::move(creation_params),
-                         std::move(loader), std::move(client),
-                         std::move(factory_clone));
+  interceptor->CreateJob(frame_token_, process_id_, is_download_,
+                         std::move(creation_params), std::move(loader),
+                         std::move(client), std::move(factory_clone));
 }
 
 void DevToolsURLLoaderFactoryProxy::StartOnIO(
@@ -593,6 +600,7 @@
 bool DevToolsURLLoaderInterceptor::CreateProxyForInterception(
     const base::UnguessableToken frame_token,
     int process_id,
+    bool is_download,
     network::mojom::URLLoaderFactoryRequest* request) const {
   if (!enabled_)
     return false;
@@ -601,7 +609,7 @@
   network::mojom::URLLoaderFactoryPtrInfo target_ptr_info;
   *request = MakeRequest(&target_ptr_info);
 
-  new DevToolsURLLoaderFactoryProxy(frame_token, process_id,
+  new DevToolsURLLoaderFactoryProxy(frame_token, process_id, is_download,
                                     std::move(original_request),
                                     std::move(target_ptr_info), weak_impl_);
   return true;
@@ -613,6 +621,7 @@
     const base::UnguessableToken& frame_token,
     int process_id,
     std::unique_ptr<CreateLoaderParameters> create_loader_params,
+    bool is_download,
     network::mojom::URLLoaderRequest loader_request,
     network::mojom::URLLoaderClientPtr client,
     network::mojom::URLLoaderFactoryPtr target_factory)
@@ -627,6 +636,7 @@
       report_upload_(!!create_loader_params->request.request_body),
       interceptor_(interceptor),
       create_loader_params_(std::move(create_loader_params)),
+      is_download_(is_download),
       client_binding_(this),
       loader_binding_(this),
       client_(std::move(client)),
@@ -1132,8 +1142,8 @@
   const network::ResourceRequest& request = create_loader_params_->request;
   request_info->is_download =
       request_info->is_navigation && request.allow_download &&
-      navigation_loader_util::IsDownload(request.url, head.headers.get(),
-                                         head.mime_type);
+      (is_download_ || navigation_loader_util::IsDownload(
+                           request.url, head.headers.get(), head.mime_type));
   NotifyClient(std::move(request_info));
 }
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.h b/content/browser/devtools/devtools_url_loader_interceptor.h
index 36f3774..3957091 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.h
+++ b/content/browser/devtools/devtools_url_loader_interceptor.h
@@ -53,6 +53,7 @@
   bool CreateProxyForInterception(
       const base::UnguessableToken frame_token,
       int process_id,  // 0 for navigation
+      bool is_download,
       network::mojom::URLLoaderFactoryRequest* request) const;
 
  private:
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 567b26c7..65963b2 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1857,10 +1857,11 @@
 bool NetworkHandler::MaybeCreateProxyForInterception(
     const base::UnguessableToken& frame_token,
     int process_id,
+    bool is_download,
     network::mojom::URLLoaderFactoryRequest* target_factory_request) {
   return url_loader_interceptor_ &&
          url_loader_interceptor_->CreateProxyForInterception(
-             frame_token, process_id, target_factory_request);
+             frame_token, process_id, is_download, target_factory_request);
 }
 
 void NetworkHandler::ApplyOverrides(net::HttpRequestHeaders* headers,
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 2669e08..a2f0a5c 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -135,6 +135,7 @@
   bool MaybeCreateProxyForInterception(
       const base::UnguessableToken& frame_token,
       int process_id,
+      bool is_download,
       network::mojom::URLLoaderFactoryRequest* target_factory_request);
 
   void ApplyOverrides(net::HttpRequestHeaders* headers,
diff --git a/content/browser/devtools/protocol/target_auto_attacher.cc b/content/browser/devtools/protocol/target_auto_attacher.cc
index e8091de..c194d82 100644
--- a/content/browser/devtools/protocol/target_auto_attacher.cc
+++ b/content/browser/devtools/protocol/target_auto_attacher.cc
@@ -170,7 +170,10 @@
 
   DCHECK(old_cross_process);
   auto it = auto_attached_hosts_.find(agent_host);
-  DCHECK(it != auto_attached_hosts_.end());
+  // This should not happen in theory, but error pages are sometimes not
+  // picked up. See https://crbug.com/836511 and https://crbug.com/817881.
+  if (it == auto_attached_hosts_.end())
+    return nullptr;
   auto_attached_hosts_.erase(it);
   detach_callback_.Run(agent_host.get());
   return nullptr;
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index 7a3e181..1ebf94f 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -392,8 +392,12 @@
   return Response::Error("Not supported");
 }
 
-Response TargetHandler::DisposeBrowserContext(const std::string& context_id,
-                                              bool* out_success) {
+Response TargetHandler::DisposeBrowserContext(const std::string& context_id) {
+  return Response::Error("Not supported");
+}
+
+Response TargetHandler::GetBrowserContexts(
+    std::unique_ptr<protocol::Array<String>>* browser_context_ids) {
   return Response::Error("Not supported");
 }
 
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h
index 6bb18ca..7d75e62a 100644
--- a/content/browser/devtools/protocol/target_handler.h
+++ b/content/browser/devtools/protocol/target_handler.h
@@ -63,8 +63,9 @@
   Response CloseTarget(const std::string& target_id,
                        bool* out_success) override;
   Response CreateBrowserContext(std::string* out_context_id) override;
-  Response DisposeBrowserContext(const std::string& context_id,
-                                 bool* out_success) override;
+  Response DisposeBrowserContext(const std::string& context_id) override;
+  Response GetBrowserContexts(
+      std::unique_ptr<protocol::Array<String>>* browser_context_ids) override;
   Response CreateTarget(const std::string& url,
                         Maybe<int> width,
                         Maybe<int> height,
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 572843f..52325be 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -296,6 +296,7 @@
 bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
     RenderFrameHostImpl* rfh,
     bool is_navigation,
+    bool is_download,
     network::mojom::URLLoaderFactoryRequest* target_factory_request) {
   FrameTreeNode* frame_tree_node = rfh->frame_tree_node();
   base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
@@ -304,9 +305,10 @@
   if (!agent_host)
     return false;
   int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID();
+  DCHECK(!is_download || is_navigation);
   for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
-    if (network->MaybeCreateProxyForInterception(frame_token, process_id,
-                                                 target_factory_request)) {
+    if (network->MaybeCreateProxyForInterception(
+            frame_token, process_id, is_download, target_factory_request)) {
       return true;
     }
   }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index afd6191..448d71a 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -73,6 +73,7 @@
   static bool WillCreateURLLoaderFactory(
       RenderFrameHostImpl* rfh,
       bool is_navigation,
+      bool is_download,
       network::mojom::URLLoaderFactoryRequest* loader_factory_request);
 
   static void OnNavigationRequestWillBeSent(
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 7d2fb68..eb14d25 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -251,7 +251,8 @@
 
 scoped_refptr<download::DownloadURLLoaderFactoryGetter>
 CreateDownloadURLLoaderFactoryGetter(StoragePartitionImpl* storage_partition,
-                                     RenderFrameHost* rfh) {
+                                     RenderFrameHost* rfh,
+                                     bool is_download) {
   network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info;
   network::mojom::URLLoaderFactoryRequest proxy_factory_request;
   if (rfh) {
@@ -259,7 +260,7 @@
     network::mojom::URLLoaderFactoryRequest devtools_factory_request =
         MakeRequest(&devtools_factory_ptr_info);
     if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-            static_cast<RenderFrameHostImpl*>(rfh), true,
+            static_cast<RenderFrameHostImpl*>(rfh), true, is_download,
             &devtools_factory_request)) {
       proxy_factory_ptr_info = std::move(devtools_factory_ptr_info);
       proxy_factory_request = std::move(devtools_factory_request);
@@ -1010,8 +1011,8 @@
       std::move(resource_request), render_process_id, render_frame_id, site_url,
       tab_url, tab_referrer_url, std::move(url_chain), std::move(response),
       std::move(cert_status), std::move(url_loader_client_endpoints),
-      CreateDownloadURLLoaderFactoryGetter(storage_partition,
-                                           render_frame_host));
+      CreateDownloadURLLoaderFactoryGetter(storage_partition, render_frame_host,
+                                           false));
 }
 
 void DownloadManagerImpl::BeginDownloadInternal(
@@ -1057,7 +1058,7 @@
               params->url(), std::move(blob_data_handle));
     } else {
       url_loader_factory_getter =
-          CreateDownloadURLLoaderFactoryGetter(storage_partition, rfh);
+          CreateDownloadURLLoaderFactoryGetter(storage_partition, rfh, true);
     }
 
     in_progress_manager_->BeginDownload(
diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc
index f6b9b80..c53d5005 100644
--- a/content/browser/find_request_manager.cc
+++ b/content/browser/find_request_manager.cc
@@ -9,6 +9,7 @@
 #include "base/containers/queue.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/associated_interface_provider_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/public/browser/guest_mode.h"
 
@@ -337,8 +338,7 @@
         // The new active match is in a different frame than the previous, so
         // the previous active frame needs to be informed (to clear its active
         // match highlighting).
-        active_frame_->Send(new FrameMsg_ClearActiveFindMatch(
-            active_frame_->GetRoutingID()));
+        ClearActiveFindMatch();
       }
       active_frame_ = rfh;
       relative_active_match_ordinal_ = active_match_ordinal;
@@ -435,6 +435,13 @@
   }
 }
 
+void FindRequestManager::ClearActiveFindMatch() {
+  blink::mojom::FindInPageAssociatedPtr active_frame_ptr;
+  active_frame_->GetRemoteAssociatedInterfaces()->GetInterface(
+      &active_frame_ptr);
+  active_frame_ptr->ClearActiveFindMatch();
+}
+
 #if defined(OS_ANDROID)
 void FindRequestManager::ActivateNearestFindResult(float x, float y) {
   if (current_session_id_ == kInvalidId)
diff --git a/content/browser/find_request_manager.h b/content/browser/find_request_manager.h
index 1939bc5..ae8a289a 100644
--- a/content/browser/find_request_manager.h
+++ b/content/browser/find_request_manager.h
@@ -14,6 +14,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/stop_find_action.h"
+#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
 #include "third_party/blink/public/web/web_find_options.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -58,6 +59,9 @@
   // called whenever a frame is discovered to no longer exist.
   void RemoveFrame(RenderFrameHost* rfh);
 
+  // Tells active frame to clear the active match highlighting.
+  void ClearActiveFindMatch();
+
 #if defined(OS_ANDROID)
   // Selects and zooms to the find result nearest to the point (x, y), defined
   // in find-in-page coordinates.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 0fd6993..f289fb7 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3794,7 +3794,8 @@
           this, false /* is_navigation */, &factory_request);
       // Keep DevTools proxy lasy, i.e. closest to the network.
       RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-          this, false, &factory_request);
+          this, false /* is_navigation */, false /* is_download */,
+          &factory_request);
       factory.second->Clone(std::move(factory_request));
       subresource_loader_factories->factories_info().emplace(
           factory.first, std::move(factory_proxy_info));
@@ -4366,7 +4367,8 @@
       this, false /* is_navigation */, &default_factory_request);
   // Keep DevTools proxy lasy, i.e. closest to the network.
   RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-      this, false, &default_factory_request);
+      this, false /* is_navigation */, false /* is_download */,
+      &default_factory_request);
   StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
       BrowserContext::GetStoragePartition(context, GetSiteInstance()));
   if (g_create_network_factory_callback_for_test.Get().is_null()) {
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index d6ae186..91bf248 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -13,6 +13,7 @@
 #include "base/debug/alias.h"
 #include "base/macros.h"
 #include "base/strings/string_util.h"
+#include "base/syslog_logging.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "components/download/public/common/download_url_parameters.h"
@@ -490,8 +491,11 @@
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
-    bad_message::ReceivedBadMessage(this,
-                                    bad_message::RFMF_SET_COOKIE_BAD_ORIGIN);
+    bad_message::BadMessageReason reason =
+        bad_message::RFMF_SET_COOKIE_BAD_ORIGIN;
+    SYSLOG(WARNING) << "Killing renderer: illegal cookie write. Reason: "
+                    << reason;
+    bad_message::ReceivedBadMessage(this, reason);
     return;
   }
 
@@ -531,8 +535,11 @@
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
-    bad_message::ReceivedBadMessage(this,
-                                    bad_message::RFMF_GET_COOKIES_BAD_ORIGIN);
+    bad_message::BadMessageReason reason =
+        bad_message::RFMF_GET_COOKIES_BAD_ORIGIN;
+    SYSLOG(WARNING) << "Killing renderer: illegal cookie read. Reason: "
+                    << reason;
+    bad_message::ReceivedBadMessage(this, reason);
     std::move(callback).Run(std::string());
     return;
   }
diff --git a/content/browser/gpu/gpu_data_manager_testing_autogen.cc b/content/browser/gpu/gpu_data_manager_testing_autogen.cc
index 43b1d39..adb983a 100644
--- a/content/browser/gpu/gpu_data_manager_testing_autogen.cc
+++ b/content/browser/gpu/gpu_data_manager_testing_autogen.cc
@@ -19,14 +19,14 @@
     {
         1,  // id
         "GpuDataManagerImplPrivateTest.GpuSideBlacklisting.0",
-        arraysize(kFeatureListForEntry1),  // features size
-        kFeatureListForEntry1,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry1),  // features size
+        kFeatureListForEntry1,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -39,6 +39,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -47,14 +49,14 @@
     {
         2,  // id
         "GpuDataManagerImplPrivateTest.GpuSideBlacklisting.1",
-        arraysize(kFeatureListForEntry2),  // features size
-        kFeatureListForEntry2,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry2),  // features size
+        kFeatureListForEntry2,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -67,6 +69,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry2,                   // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -75,14 +79,14 @@
     {
         3,  // id
         "GpuDataManagerImplPrivateTest.GpuSideBlacklistingWebGL.0",
-        arraysize(kFeatureListForEntry3),  // features size
-        kFeatureListForEntry3,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry3),  // features size
+        kFeatureListForEntry3,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -95,6 +99,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -103,14 +109,14 @@
     {
         4,  // id
         "GpuDataManagerImplPrivateTest.GpuSideBlacklistingWebGL.1",
-        arraysize(kFeatureListForEntry4),  // features size
-        kFeatureListForEntry4,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry4),  // features size
+        kFeatureListForEntry4,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -123,6 +129,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry4,                   // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -131,14 +139,14 @@
     {
         5,  // id
         "GpuDataManagerImplPrivateTest.GpuSideException",
-        arraysize(kFeatureListForEntry5),  // features size
-        kFeatureListForEntry5,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry5),  // features size
+        kFeatureListForEntry5,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -151,22 +159,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry5),  // exceptions count
-        kExceptionsForEntry5,             // exceptions
+        base::size(kExceptionsForEntry5),  // exceptions count
+        kExceptionsForEntry5,              // exceptions
     },
     {
         6,  // id
         "GpuDataManagerImplPrivateTest.BlacklistAllFeatures",
-        arraysize(kFeatureListForEntry6),  // features size
-        kFeatureListForEntry6,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry6),  // features size
+        kFeatureListForEntry6,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -179,6 +189,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -187,14 +199,14 @@
     {
         7,  // id
         "GpuDataManagerImplPrivateTest.UpdateActiveGpu",
-        arraysize(kFeatureListForEntry7),  // features size
-        kFeatureListForEntry7,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry7),  // features size
+        kFeatureListForEntry7,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -207,6 +219,8 @@
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
diff --git a/content/browser/gpu/gpu_data_manager_testing_exceptions_autogen.h b/content/browser/gpu/gpu_data_manager_testing_exceptions_autogen.h
index 12a2a5491..9c282ee 100644
--- a/content/browser/gpu/gpu_data_manager_testing_exceptions_autogen.h
+++ b/content/browser/gpu/gpu_data_manager_testing_exceptions_autogen.h
@@ -25,6 +25,8 @@
         nullptr,                                // driver info
         &kGLStringsForEntry5Exception0,         // GL strings
         nullptr,                                // machine model info
+        0,                                      // gpu_series size
+        nullptr,                                // gpu_series
         nullptr,                                // more conditions
     },
 };
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 41a7ad5..266e6b74 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -445,7 +445,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
     ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
         ui::mojom::kServiceName, std::move(request));
     return;
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 76e8358a..494ecda0 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -433,7 +433,8 @@
                                            "valid.js",
                                            "json-list.js",
                                            "nosniff.json-list.js",
-                                           "js-html-polyglot.html"};
+                                           "js-html-polyglot.html",
+                                           "js-html-polyglot2.html"};
   for (const char* resource : sniff_allowed_resources) {
     SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
     base::HistogramTester histograms;
diff --git a/content/browser/loader/cross_site_document_resource_handler_unittest.cc b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
index 1625ce1a..7f66b56 100644
--- a/content/browser/loader/cross_site_document_resource_handler_unittest.cc
+++ b/content/browser/loader/cross_site_document_resource_handler_unittest.cc
@@ -850,10 +850,10 @@
         false,                                  // include_no_sniff_header
         false,                                  // simulate_range_response
         AccessControlAllowOriginHeader::kOmit,  // cors_response
-        {"    <!--", "\t -", "-", "->", "<", "s", "c", "r", "i", "p",
+        {"    <!--", "\t -", "-", "->", "\n", "<", "s", "c", "r", "i", "p",
          "t"},            // packets
         Verdict::kBlock,  // verdict
-        10,               // verdict_packet
+        11,               // verdict_packet
     },
     {
         "Blocked: slow-arriving html with commented-out xml tag",
@@ -867,8 +867,8 @@
         false,                                  // include_no_sniff_header
         false,                                  // simulate_range_response
         AccessControlAllowOriginHeader::kOmit,  // cors_response
-        {"    <!--", " <?xml ", "-->", "<", "h", "e", "a", "d"},  // packets
-        Verdict::kBlock,                                          // verdict
+        {"    <!--", " <?xml ", "-->\n", "<", "h", "e", "a", "d"},  // packets
+        Verdict::kBlock,                                            // verdict
         7,  // verdict_packet
     },
     {
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index c8d2ef8..3cc782fc 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -1231,7 +1231,8 @@
         frame_tree_node->current_frame_host(), true /* is_navigation */,
         &factory_request);
     if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-            frame_tree_node->current_frame_host(), true, &factory_request)) {
+            frame_tree_node->current_frame_host(), true, false,
+            &factory_request)) {
       use_proxy = true;
     }
     if (use_proxy) {
diff --git a/content/browser/media/audio_input_stream_broker.cc b/content/browser/media/audio_input_stream_broker.cc
index d4bbd8c..8b5ae78 100644
--- a/content/browser/media/audio_input_stream_broker.cc
+++ b/content/browser/media/audio_input_stream_broker.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/media/media_internals.h"
 #include "content/public/browser/browser_thread.h"
@@ -16,6 +17,7 @@
 #include "content/public/common/content_client.h"
 #include "media/audio/audio_logging.h"
 #include "media/base/media_switches.h"
+#include "media/base/user_input_monitor.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 #if defined(OS_CHROMEOS)
@@ -61,15 +63,19 @@
     params_.set_format(media::AudioParameters::AUDIO_FAKE);
   }
 
+  BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
+  // May be null in unit tests.
+  if (!browser_main_loop)
+    return;
+
 #if defined(OS_CHROMEOS)
   if (params_.channel_layout() ==
       media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
-    BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
-
-    // May be null in unit tests.
-    if (browser_main_loop)
       browser_main_loop->keyboard_mic_registration()->Register();
   }
+#else
+  user_input_monitor_ = static_cast<media::UserInputMonitorBase*>(
+      browser_main_loop->user_input_monitor());
 #endif
 }
 
@@ -85,6 +91,9 @@
     if (browser_main_loop)
       browser_main_loop->keyboard_mic_registration()->Deregister();
   }
+#else
+  // Check that DisableKeyPressMonitoring() was called.
+  DCHECK(!user_input_monitor_);
 #endif
 
   auto* process_host = RenderProcessHost::FromID(render_process_id());
@@ -100,6 +109,12 @@
   DCHECK(!observer_binding_.is_bound());
   DCHECK(!client_request_);
 
+  base::ReadOnlySharedMemoryRegion key_press_count_buffer;
+  if (user_input_monitor_) {
+    key_press_count_buffer =
+        user_input_monitor_->EnableKeyPressMonitoringWithMapping();
+  }
+
   media::mojom::AudioInputStreamClientPtr client;
   client_request_ = mojo::MakeRequest(&client);
 
@@ -126,7 +141,7 @@
           media::AudioLogFactory::AudioComponent::AUDIO_INPUT_CONTROLLER,
           log_component_id, render_process_id(), render_frame_id()),
       device_id_, params_, shared_memory_count_, enable_agc_,
-      mojo::ScopedSharedBufferHandle(),
+      mojo::WrapReadOnlySharedMemoryRegion(std::move(key_press_count_buffer)),
       base::BindOnce(&AudioInputStreamBroker::StreamCreated,
                      weak_ptr_factory_.GetWeakPtr(), std::move(stream)));
 }
@@ -155,6 +170,14 @@
 
 void AudioInputStreamBroker::Cleanup() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (user_input_monitor_) {
+    user_input_monitor_->DisableKeyPressMonitoring();
+
+    // Set to nullptr to check that DisableKeyPressMonitoring() was called
+    // before destructor
+    user_input_monitor_ = nullptr;
+  }
+
   std::move(deleter_).Run(this);
 }
 
diff --git a/content/browser/media/audio_input_stream_broker.h b/content/browser/media/audio_input_stream_broker.h
index 4697479..52f5ab0 100644
--- a/content/browser/media/audio_input_stream_broker.h
+++ b/content/browser/media/audio_input_stream_broker.h
@@ -17,6 +17,10 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/audio/public/mojom/stream_factory.mojom.h"
 
+namespace media {
+class UserInputMonitorBase;
+}
+
 namespace content {
 
 // AudioInputStreamBroker is used to broker a connection between a client
@@ -53,6 +57,7 @@
   media::AudioParameters params_;
   const uint32_t shared_memory_count_;
   const bool enable_agc_;
+  media::UserInputMonitorBase* user_input_monitor_ = nullptr;
 
   DeleterCallback deleter_;
 
diff --git a/content/browser/media/forwarding_audio_stream_factory.cc b/content/browser/media/forwarding_audio_stream_factory.cc
index e94f4b3..30edea5 100644
--- a/content/browser/media/forwarding_audio_stream_factory.cc
+++ b/content/browser/media/forwarding_audio_stream_factory.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "content/browser/media/capture/audio_mirroring_manager.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -34,6 +35,17 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
+// static
+ForwardingAudioStreamFactory* ForwardingAudioStreamFactory::ForFrame(
+    RenderFrameHost* frame) {
+  auto* contents =
+      static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(frame));
+  if (!contents)
+    return nullptr;
+
+  return contents->GetAudioStreamFactory();
+}
+
 void ForwardingAudioStreamFactory::CreateInputStream(
     RenderFrameHost* frame,
     const std::string& device_id,
diff --git a/content/browser/media/forwarding_audio_stream_factory.h b/content/browser/media/forwarding_audio_stream_factory.h
index fce7320d..ea0748aa 100644
--- a/content/browser/media/forwarding_audio_stream_factory.h
+++ b/content/browser/media/forwarding_audio_stream_factory.h
@@ -43,6 +43,11 @@
 
   ~ForwardingAudioStreamFactory() final;
 
+  // Returns the ForwardingAudioStreamFactory which takes care of stream
+  // creation for |frame|. Returns null if |frame| is null or if the frame
+  // doesn't belong to a WebContents.
+  static ForwardingAudioStreamFactory* ForFrame(RenderFrameHost* frame);
+
   const base::UnguessableToken& group_id() { return group_id_; }
 
   // TODO(https://crbug.com/803102): Add loopback and muting streams.
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index b29284a..9e9fd954 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -195,6 +195,14 @@
 #endif
 }
 
+void MediaInterfaceProxy::CreateDecryptor(
+    int cdm_id,
+    media::mojom::DecryptorRequest request) {
+  InterfaceFactory* factory = GetMediaInterfaceFactory();
+  if (factory)
+    factory->CreateDecryptor(cdm_id, std::move(request));
+}
+
 void MediaInterfaceProxy::CreateCdmProxy(
     const std::string& cdm_guid,
     media::mojom::CdmProxyRequest request) {
diff --git a/content/browser/media/media_interface_proxy.h b/content/browser/media/media_interface_proxy.h
index 08b7661..ce7e4dfb 100644
--- a/content/browser/media/media_interface_proxy.h
+++ b/content/browser/media/media_interface_proxy.h
@@ -51,6 +51,8 @@
                       media::mojom::RendererRequest request) final;
   void CreateCdm(const std::string& key_system,
                  media::mojom::ContentDecryptionModuleRequest request) final;
+  void CreateDecryptor(int cdm_id,
+                       media::mojom::DecryptorRequest request) final;
   void CreateCdmProxy(const std::string& cdm_guid,
                       media::mojom::CdmProxyRequest request) final;
 
diff --git a/content/browser/oop_browsertest.cc b/content/browser/oop_browsertest.cc
index e3502bb0..5fe99fd 100644
--- a/content/browser/oop_browsertest.cc
+++ b/content/browser/oop_browsertest.cc
@@ -35,7 +35,7 @@
     command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
     command_line->AppendSwitch(switches::kEnableOOPRasterization);
 
-    const bool use_gpu_in_tests = !features::IsMusEnabled();
+    const bool use_gpu_in_tests = !features::IsMashEnabled();
     if (use_gpu_in_tests)
       command_line->AppendSwitch(switches::kUseGpuInTests);
   }
diff --git a/content/browser/picture_in_picture/overlay_surface_embedder.cc b/content/browser/picture_in_picture/overlay_surface_embedder.cc
index 27263a6..5a7da3d 100644
--- a/content/browser/picture_in_picture/overlay_surface_embedder.cc
+++ b/content/browser/picture_in_picture/overlay_surface_embedder.cc
@@ -40,15 +40,21 @@
 void OverlaySurfaceEmbedder::UpdateLayerBounds() {
   // Update the size and position of the video to stretch on the entire window.
   gfx::Size window_size = window_->GetBounds().size();
-  video_layer_->SetBounds(gfx::Rect(gfx::Point(0, 0), window_size));
+  gfx::Rect window_bounds = gfx::Rect(gfx::Point(0, 0), window_size);
+  video_layer_->SetBounds(window_bounds);
   video_layer_->SetSurfaceSize(window_size);
 
   // Update the size and position of controls.
+  controls_background_layer_->SetBounds(window_bounds);
   close_controls_layer_->SetBounds(window_->GetCloseControlsBounds());
   play_pause_controls_layer_->SetBounds(window_->GetPlayPauseControlsBounds());
 }
 
 void OverlaySurfaceEmbedder::AddControlsLayers() {
+  controls_background_layer_ = window_->GetControlsBackgroundLayer();
+  controls_background_layer_->SetBounds(
+      gfx::Rect(gfx::Point(0, 0), window_->GetBounds().size()));
+
   close_controls_layer_ = window_->GetCloseControlsLayer();
   close_controls_layer_->SetFillsBoundsOpaquely(false);
   close_controls_layer_->SetBounds(window_->GetCloseControlsBounds());
@@ -57,6 +63,7 @@
   play_pause_controls_layer_->SetFillsBoundsOpaquely(false);
   play_pause_controls_layer_->SetBounds(window_->GetPlayPauseControlsBounds());
 
+  window_->GetLayer()->Add(controls_background_layer_);
   window_->GetLayer()->Add(close_controls_layer_);
   window_->GetLayer()->Add(play_pause_controls_layer_);
 }
diff --git a/content/browser/picture_in_picture/overlay_surface_embedder.h b/content/browser/picture_in_picture/overlay_surface_embedder.h
index d4a2ae9..d4b977f 100644
--- a/content/browser/picture_in_picture/overlay_surface_embedder.h
+++ b/content/browser/picture_in_picture/overlay_surface_embedder.h
@@ -37,6 +37,7 @@
 
   // Owned by the OverlayWindow implementation.
   ui::Layer* video_layer_ = nullptr;
+  ui::Layer* controls_background_layer_ = nullptr;
   ui::Layer* close_controls_layer_ = nullptr;
   ui::Layer* play_pause_controls_layer_ = nullptr;
 
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
index c0c092a..c3ed32b 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
@@ -57,8 +57,6 @@
 
   media_web_contents_observer_ = static_cast<WebContentsImpl* const>(initiator_)
                                      ->media_web_contents_observer();
-  media_player_id_ =
-      media_web_contents_observer_->GetPictureInPictureVideoMediaPlayerId();
 
   window_ =
       GetContentClient()->browser()->CreateWindowForPictureInPicture(this);
@@ -88,6 +86,13 @@
   DCHECK(surface_id.is_valid());
   surface_id_ = surface_id;
 
+  // Update the media player id in step with the video surface id. If the
+  // surface id was updated for the same video, this is a no-op. This could
+  // be updated for a different video if another media player on the same
+  // |initiator_| enters Picture-in-Picture mode.
+  media_player_id_ =
+      media_web_contents_observer_->GetPictureInPictureVideoMediaPlayerId();
+
   window_->UpdateVideoSize(natural_size);
 
   if (!embedder_)
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.h b/content/browser/renderer_host/browser_compositor_view_mac.h
index 7f0ce8a..f823af11 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.h
+++ b/content/browser/renderer_host/browser_compositor_view_mac.h
@@ -212,6 +212,8 @@
 
   uint32_t capture_sequence_number_ = 0;
 
+  bool is_first_navigation_ = true;
+
   base::WeakPtrFactory<BrowserCompositorMac> weak_factory_;
 };
 
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index c1c7a42..a03b35db 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -529,12 +529,17 @@
 }
 
 void BrowserCompositorMac::DidNavigate() {
-  const viz::LocalSurfaceId& new_local_surface_id =
-      dfh_local_surface_id_allocator_.GenerateId();
+  // The first navigation does not need a new LocalSurfaceID. The renderer can
+  // use the ID that was already provided.
+  if (!is_first_navigation_) {
+    const viz::LocalSurfaceId& new_local_surface_id =
+        dfh_local_surface_id_allocator_.GenerateId();
+    delegated_frame_host_->SynchronizeVisualProperties(
+        new_local_surface_id, dfh_size_dip_,
+        cc::DeadlinePolicy::UseExistingDeadline());
+  }
+  is_first_navigation_ = false;
   client_->SynchronizeVisualProperties();
-  delegated_frame_host_->SynchronizeVisualProperties(
-      new_local_surface_id, dfh_size_dip_,
-      cc::DeadlinePolicy::UseExistingDeadline());
   delegated_frame_host_->DidNavigate();
 }
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 4218e10..e5c4d8d 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -257,7 +257,7 @@
  public:
   AndroidOutputSurface(
       scoped_refptr<ui::ContextProviderCommandBuffer> context_provider,
-      base::Closure swap_buffers_callback)
+      base::RepeatingCallback<void(gfx::Size)> swap_buffers_callback)
       : viz::OutputSurface(std::move(context_provider)),
         swap_buffers_callback_(std::move(swap_buffers_callback)),
         overlay_candidate_validator_(
@@ -272,9 +272,10 @@
     if (LatencyInfoHasSnapshotRequest(frame.latency_info))
       GetCommandBufferProxy()->SetSnapshotRequested();
 
-    auto callback = base::BindOnce(
-        &AndroidOutputSurface::OnSwapBuffersCompleted,
-        weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info));
+    auto callback =
+        base::BindOnce(&AndroidOutputSurface::OnSwapBuffersCompleted,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       std::move(frame.latency_info), frame.size);
     uint32_t flags = 0;
     if (frame.need_presentation_feedback)
       flags |= gpu::SwapBuffersFlags::kPresentationFeedback;
@@ -347,9 +348,10 @@
   }
 
   void OnSwapBuffersCompleted(std::vector<ui::LatencyInfo> latency_info,
+                              gfx::Size swap_size,
                               const gpu::SwapBuffersCompleteParams& params) {
     client_->DidReceiveSwapBuffersAck(params.swap_response.swap_id);
-    swap_buffers_callback_.Run();
+    swap_buffers_callback_.Run(swap_size);
     UpdateLatencyInfoOnSwap(params.swap_response, &latency_info);
     RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info);
     latency_tracker_.OnGpuSwapBuffersCompleted(latency_info);
@@ -362,7 +364,7 @@
 
  private:
   viz::OutputSurfaceClient* client_ = nullptr;
-  base::Closure swap_buffers_callback_;
+  base::RepeatingCallback<void(gfx::Size)> swap_buffers_callback_;
   std::unique_ptr<viz::OverlayCandidateValidator> overlay_candidate_validator_;
   ui::LatencyTracker latency_tracker_;
 
@@ -852,6 +854,7 @@
   viz::RendererSettings renderer_settings;
   renderer_settings.allow_antialiasing = false;
   renderer_settings.highp_threshold_min = 2048;
+  renderer_settings.auto_resize_output_surface = false;
   auto* gpu_memory_buffer_manager = BrowserMainLoop::GetInstance()
                                         ->gpu_channel_establish_factory()
                                         ->GetGpuMemoryBufferManager();
@@ -880,8 +883,8 @@
   host_->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
 }
 
-void CompositorImpl::DidSwapBuffers() {
-  client_->DidSwapBuffers();
+void CompositorImpl::DidSwapBuffers(gfx::Size swap_size) {
+  client_->DidSwapBuffers(swap_size);
 }
 
 cc::UIResourceId CompositorImpl::CreateUIResource(
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index a037726..7237d4f 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -160,7 +160,7 @@
   void InitializeDisplay(
       std::unique_ptr<viz::OutputSurface> display_output_surface,
       scoped_refptr<viz::ContextProvider> context_provider);
-  void DidSwapBuffers();
+  void DidSwapBuffers(gfx::Size swap_size);
 
   bool HavePendingReadbacks();
 
diff --git a/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc b/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
index e9960e21..f3565b6c 100644
--- a/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_delegate_impl_unittest.cc
@@ -104,14 +104,14 @@
   MOCK_METHOD1(OnStreamError, void(int));
 };
 
-class MockUserInputMonitor : public media::UserInputMonitorBase {
+class MockUserInputMonitor : public media::UserInputMonitor {
  public:
   MockUserInputMonitor() {}
 
   uint32_t GetKeyPressCount() const override { return 0; }
 
-  MOCK_METHOD0(StartKeyboardMonitoring, void());
-  MOCK_METHOD0(StopKeyboardMonitoring, void());
+  MOCK_METHOD0(EnableKeyPressMonitoring, void());
+  MOCK_METHOD0(DisableKeyPressMonitoring, void());
 };
 
 class MockAudioInputStream : public media::AudioInputStream {
diff --git a/content/browser/renderer_host/overscroll_configuration.cc b/content/browser/renderer_host/overscroll_configuration.cc
index 51b4e0d..34496b1 100644
--- a/content/browser/renderer_host/overscroll_configuration.cc
+++ b/content/browser/renderer_host/overscroll_configuration.cc
@@ -29,6 +29,10 @@
 bool g_is_touchpad_overscroll_history_navigation_enabled_initialized = false;
 bool g_touchpad_overscroll_history_navigation_enabled = false;
 
+// On Windows, we only process 0.3 second inertial events then cancel the
+// overscroll if it is not completed yet.
+int g_max_inertial_events_before_overscroll_cancellation_in_ms = 300;
+
 float GetStartThresholdMultiplier() {
   base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
   if (!cmd->HasSwitch(switches::kOverscrollStartThreshold))
@@ -148,4 +152,11 @@
   g_is_touchpad_overscroll_history_navigation_enabled_initialized = false;
 }
 
+// static
+base::TimeDelta
+OverscrollConfig::MaxInertialEventsBeforeOverscrollCancellation() {
+  return base::TimeDelta::FromMilliseconds(
+      g_max_inertial_events_before_overscroll_cancellation_in_ms);
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc
index 4e323f3..a4bb1af 100644
--- a/content/browser/renderer_host/overscroll_controller.cc
+++ b/content/browser/renderer_host/overscroll_controller.cc
@@ -29,6 +29,16 @@
   return gesture.SourceDevice() == blink::kWebGestureDeviceTouchpad;
 }
 
+bool IsGestureScrollUpdateInertialEvent(const blink::WebInputEvent& event) {
+  if (event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
+    return false;
+
+  const blink::WebGestureEvent& gesture =
+      static_cast<const blink::WebGestureEvent&>(event);
+  return gesture.data.scroll_update.inertial_phase ==
+         blink::WebGestureEvent::kMomentumPhase;
+}
+
 float ClampAbsoluteValue(float value, float max_abs) {
   DCHECK_LT(0.f, max_abs);
   return std::max(-max_abs, std::min(value, max_abs));
@@ -86,17 +96,10 @@
 
 bool OverscrollController::ShouldIgnoreInertialEvent(
     const blink::WebInputEvent& event) const {
-  if (!ignore_following_inertial_events_ ||
-      event.GetType() != blink::WebInputEvent::kGestureScrollUpdate) {
-    return false;
+  return ignore_following_inertial_events_ &&
+         IsGestureScrollUpdateInertialEvent(event);
   }
 
-  const blink::WebGestureEvent& gesture =
-      static_cast<const blink::WebGestureEvent&>(event);
-  return gesture.data.scroll_update.inertial_phase ==
-         blink::WebGestureEvent::kMomentumPhase;
-}
-
 bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
   if (!ShouldProcessEvent(event))
     return false;
@@ -108,6 +111,7 @@
 
   if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
     ignore_following_inertial_events_ = false;
+    first_inertial_event_time_.reset();
     time_since_last_ignored_scroll_ =
         event.TimeStamp() - last_ignored_scroll_time_;
     // Will handle events when processing ACKs to ensure the correct order.
@@ -379,10 +383,29 @@
     case blink::WebInputEvent::kGestureScrollUpdate: {
       const blink::WebGestureEvent& gesture =
           static_cast<const blink::WebGestureEvent&>(event);
+      bool is_gesture_scroll_update_inertial_event =
+          IsGestureScrollUpdateInertialEvent(event);
       event_processed = ProcessOverscroll(
           gesture.data.scroll_update.delta_x,
           gesture.data.scroll_update.delta_y,
-          gesture.SourceDevice() == blink::kWebGestureDeviceTouchpad);
+          gesture.SourceDevice() == blink::kWebGestureDeviceTouchpad,
+          is_gesture_scroll_update_inertial_event);
+      if (is_gesture_scroll_update_inertial_event) {
+        // Record the timestamp of first inertial event.
+        if (!first_inertial_event_time_) {
+          first_inertial_event_time_ = event.TimeStamp();
+          break;
+        }
+        base::TimeDelta inertial_event_interval =
+            event.TimeStamp() - first_inertial_event_time_.value();
+        if (inertial_event_interval >=
+            OverscrollConfig::MaxInertialEventsBeforeOverscrollCancellation()) {
+          ignore_following_inertial_events_ = true;
+          // Reset overscroll state if fling didn't complete the overscroll
+          // gesture within the first 20 inertial events.
+          Cancel();
+        }
+      }
       break;
     }
     case blink::WebInputEvent::kGestureFlingStart: {
@@ -422,10 +445,15 @@
 
 bool OverscrollController::ProcessOverscroll(float delta_x,
                                              float delta_y,
-                                             bool is_touchpad) {
+                                             bool is_touchpad,
+                                             bool is_inertial) {
   if (scroll_state_ == ScrollState::CONTENT_CONSUMING)
     return false;
 
+  // Do not start overscroll for inertial events.
+  if (overscroll_mode_ == OVERSCROLL_NONE && is_inertial)
+    return false;
+
   overscroll_delta_x_ += delta_x;
   overscroll_delta_y_ += delta_y;
 
diff --git a/content/browser/renderer_host/overscroll_controller.h b/content/browser/renderer_host/overscroll_controller.h
index 9479f5f..dbc94c17 100644
--- a/content/browser/renderer_host/overscroll_controller.h
+++ b/content/browser/renderer_host/overscroll_controller.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/time/time.h"
 #include "cc/input/overscroll_behavior.h"
 #include "content/common/content_export.h"
@@ -112,7 +113,10 @@
   // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_|
   // and |overscroll_delta_y_|). Returns true if overscroll was handled by the
   // delegate.
-  bool ProcessOverscroll(float delta_x, float delta_y, bool is_touchpad);
+  bool ProcessOverscroll(float delta_x,
+                         float delta_y,
+                         bool is_touchpad,
+                         bool is_inertial);
 
   // Completes the desired action from the current gesture.
   void CompleteAction();
@@ -162,8 +166,9 @@
   bool wheel_scroll_latching_enabled_;
 
   // A inertial scroll (fling) event may complete an overscroll gesture and
-  // navigate to a new page, but the inertial scroll can continue to generate
-  // scroll-update events. These events need to be ignored.
+  // navigate to a new page or cancel the overscroll animation. In both cases
+  // inertial scroll can continue to generate scroll-update events. These events
+  // need to be ignored.
   bool ignore_following_inertial_events_ = false;
 
   // Specifies whether last overscroll was ignored, either due to a command line
@@ -177,6 +182,14 @@
   // of the current one.
   base::TimeDelta time_since_last_ignored_scroll_;
 
+  // On Windows, we don't generate the inertial events (fling) but receive them
+  // from Win API. In some cases, we get a long tail of inertial events for a
+  // couple of seconds. The overscroll animation feels like stuck in these
+  // cases. So we only process 0.3 second inertial events then cancel the
+  // overscroll if it is not completed yet.
+  // Timestamp for the first inertial event (fling) in current stream.
+  base::Optional<base::TimeTicks> first_inertial_event_time_;
+
   DISALLOW_COPY_AND_ASSIGN(OverscrollController);
 };
 
diff --git a/content/browser/renderer_host/overscroll_controller_unittest.cc b/content/browser/renderer_host/overscroll_controller_unittest.cc
index aefe4b3..166d1fe3 100644
--- a/content/browser/renderer_host/overscroll_controller_unittest.cc
+++ b/content/browser/renderer_host/overscroll_controller_unittest.cc
@@ -201,6 +201,75 @@
       100, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
 }
 
+// Ensure inertial gesture scroll update can not start overscroll.
+TEST_F(OverscrollControllerTest, InertialGSUsDoNotStartOverscroll) {
+  base::TimeTicks timestamp =
+      blink::WebInputEvent::GetStaticTimeStampForTests();
+  // Inertial update event complete the overscroll action.
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      100, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
+  SimulateAck(false);
+  EXPECT_EQ(OVERSCROLL_NONE, controller_mode());
+  EXPECT_EQ(OverscrollSource::NONE, controller_source());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+}
+
+// After 300ms inertial gesture scroll updates, overscroll must get cancelled
+// if not completed.
+TEST_F(OverscrollControllerTest, OnlyProcessLimitedInertialGSUEvents) {
+  base::TimeTicks timestamp =
+      blink::WebInputEvent::GetStaticTimeStampForTests();
+
+  EXPECT_FALSE(SimulateGestureEvent(blink::WebInputEvent::kGestureScrollBegin,
+                                    blink::kWebGestureDeviceTouchpad,
+                                    timestamp));
+  SimulateAck(false);
+
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      61, 0, blink::kWebGestureDeviceTouchpad, timestamp, false));
+  SimulateAck(false);
+  EXPECT_EQ(OVERSCROLL_EAST, controller_mode());
+  EXPECT_EQ(OverscrollSource::TOUCHPAD, controller_source());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+
+  // First inertial.
+  timestamp += base::TimeDelta::FromSeconds(1);
+  EXPECT_TRUE(SimulateGestureScrollUpdate(
+      1, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
+  SimulateAck(true);
+  EXPECT_EQ(OVERSCROLL_EAST, controller_mode());
+  EXPECT_EQ(OverscrollSource::TOUCHPAD, controller_source());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+
+  // Not cancel in 10ms.
+  timestamp += base::TimeDelta::FromMilliseconds(10);
+  EXPECT_TRUE(SimulateGestureScrollUpdate(
+      1, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
+  SimulateAck(true);
+  EXPECT_EQ(OVERSCROLL_EAST, controller_mode());
+  EXPECT_EQ(OverscrollSource::TOUCHPAD, controller_source());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+
+  // Cancel after 300ms.
+  timestamp += base::TimeDelta::FromMilliseconds(291);
+  EXPECT_TRUE(SimulateGestureScrollUpdate(
+      1, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
+  SimulateAck(true);
+  EXPECT_EQ(OVERSCROLL_NONE, controller_mode());
+  EXPECT_EQ(OverscrollSource::NONE, controller_source());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+
+  // Next event should be ignored.
+  timestamp += base::TimeDelta::FromMilliseconds(100);
+  EXPECT_TRUE(SimulateGestureScrollUpdate(
+      1, 0, blink::kWebGestureDeviceTouchpad, timestamp, true));
+}
+
 // Verifies that when pull-to-refresh is disabled, it is not triggered for
 // neither touchpad nor touchscreen.
 TEST_F(OverscrollControllerTest, PullToRefreshDisabled) {
diff --git a/content/browser/renderer_host/pepper/ssl_context_helper.cc b/content/browser/renderer_host/pepper/ssl_context_helper.cc
index 0596302..1d6fb4c9 100644
--- a/content/browser/renderer_host/pepper/ssl_context_helper.cc
+++ b/content/browser/renderer_host/pepper/ssl_context_helper.cc
@@ -35,7 +35,7 @@
 
 net::CTPolicyEnforcer* SSLContextHelper::GetCTPolicyEnforcer() {
   if (!ct_policy_enforcer_)
-    ct_policy_enforcer_.reset(new net::CTPolicyEnforcer());
+    ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer());
   return ct_policy_enforcer_.get();
 }
 
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 87436fac..2cec58a 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -264,8 +264,15 @@
 }
 
 bool RenderWidgetHostViewAndroid::SynchronizeVisualProperties() {
-  if (delegated_frame_host_)
+  if (delegated_frame_host_) {
     delegated_frame_host_->SynchronizeVisualProperties();
+
+    // TODO(ericrk): This can be removed once surface synchronization is
+    // enabled. https://crbug.com/835102
+    delegated_frame_host_->PixelSizeWillChange(
+        GetCompositorViewportPixelSize());
+  }
+
   return host()->SynchronizeVisualProperties();
 }
 
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 02a7b71..6a0753f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1714,7 +1714,7 @@
 void RenderWidgetHostViewAura::ScheduleEmbed(
     ui::mojom::WindowTreeClientPtr client,
     base::OnceCallback<void(const base::UnguessableToken&)> callback) {
-  DCHECK(features::IsMusEnabled());
+  DCHECK(features::IsMashEnabled());
   aura::Env::GetInstance()->ScheduleEmbed(std::move(client),
                                           std::move(callback));
 }
@@ -1935,7 +1935,7 @@
   if (frame_sink_id_.is_valid())
     window_->SetEmbedFrameSinkId(frame_sink_id_);
 
-  if (!features::IsMusEnabled())
+  if (!features::IsMashEnabled())
     return;
 
   // Embed the renderer into the Window.
@@ -2500,10 +2500,17 @@
 }
 
 void RenderWidgetHostViewAura::DidNavigate() {
-  SynchronizeVisualProperties(cc::DeadlinePolicy::UseExistingDeadline(),
-                              base::nullopt);
+  // The first navigation does not need a new LocalSurfaceID. The renderer can
+  // use the ID that was already provided.
+  if (is_first_navigation_) {
+    SyncSurfaceProperties(cc::DeadlinePolicy::UseExistingDeadline());
+  } else {
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseExistingDeadline(),
+                                base::nullopt);
+  }
   if (delegated_frame_host_)
     delegated_frame_host_->DidNavigate();
+  is_first_navigation_ = false;
 }
 
 // static
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index ae64f09f..4193729a 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -682,6 +682,8 @@
   ui::EventPointerType last_pointer_type_before_focus_ =
       ui::EventPointerType::POINTER_TYPE_UNKNOWN;
 
+  bool is_first_navigation_ = true;
+
   base::WeakPtrFactory<RenderWidgetHostViewAura> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura);
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 bcf071ec..67f8a47b 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
@@ -6088,16 +6088,31 @@
   if (base::FeatureList::IsEnabled(features::kMash))
     return;
 
+  constexpr base::TimeDelta kTimeout = base::TimeDelta::FromMicroseconds(10);
+
   view_->InitAsChild(nullptr);
   aura::client::ParentWindowWithContext(
       view_->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
 
-  viz::LocalSurfaceId id1 = view_->GetLocalSurfaceId();
-  EXPECT_TRUE(id1.is_valid());
+  widget_host_->set_new_content_rendering_delay_for_testing(kTimeout);
 
-  widget_host_->set_new_content_rendering_delay_for_testing(
-      base::TimeDelta::FromMicroseconds(10));
+  viz::LocalSurfaceId id0 = view_->GetLocalSurfaceId();
+  EXPECT_TRUE(id0.is_valid());
+
+  // No new LocalSurfaceId should be allocated for the first navigation but the
+  // timer should fire.
+  widget_host_->DidNavigate(1);
+  viz::LocalSurfaceId id1 = view_->GetLocalSurfaceId();
+  EXPECT_EQ(id0, id1);
+  {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), 2 * kTimeout);
+    run_loop.Run();
+  }
+  EXPECT_TRUE(widget_host_->new_content_rendering_timeout_fired());
+  widget_host_->reset_new_content_rendering_timeout_fired();
 
   // Start the timer. Verify that a new LocalSurfaceId is allocated.
   widget_host_->DidNavigate(5);
@@ -6113,8 +6128,7 @@
   {
     base::RunLoop run_loop;
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, run_loop.QuitClosure(),
-        base::TimeDelta::FromMicroseconds(20));
+        FROM_HERE, run_loop.QuitClosure(), 2 * kTimeout);
     run_loop.Run();
   }
   EXPECT_TRUE(widget_host_->new_content_rendering_timeout_fired());
@@ -6131,8 +6145,7 @@
   {
     base::RunLoop run_loop;
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, run_loop.QuitClosure(),
-        base::TimeDelta::FromMicroseconds(20));
+        FROM_HERE, run_loop.QuitClosure(), 2 * kTimeout);
     run_loop.Run();
   }
   EXPECT_FALSE(widget_host_->new_content_rendering_timeout_fired());
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 457ad28..4563b7f 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
@@ -171,7 +171,7 @@
   }
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
     frame_connector_->EmbedRendererWindowTreeClientInParent(
         GetWindowTreeClientFromRenderer());
   }
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index 55e5293..af1a24e 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -44,7 +44,7 @@
 #elif defined(OS_MACOSX)
     registry_.AddInterface(base::BindRepeating(&FontLoaderDispatcher::Create));
 #endif
-    if (!features::IsMusEnabled()) {
+    if (!features::IsMashEnabled()) {
       // 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 47d8042f8..ea66245 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -91,14 +91,6 @@
 #include "ui/aura/env.h"
 #endif
 
-#if BUILDFLAG(ENABLE_MUS)
-#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
-#include "content/public/browser/discardable_shared_memory_manager.h"
-#include "services/ui/common/image_cursors_set.h"
-#include "services/ui/public/interfaces/constants.mojom.h"
-#include "services/ui/service.h"
-#endif
-
 namespace content {
 
 namespace {
@@ -282,44 +274,6 @@
 #endif
 }
 
-#if BUILDFLAG(ENABLE_MUS)
-std::unique_ptr<service_manager::Service> CreateEmbeddedUIService(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    base::WeakPtr<ui::ImageCursorsSet> image_cursors_set_weak_ptr,
-    discardable_memory::DiscardableSharedMemoryManager* memory_manager) {
-  ui::Service::InitParams params;
-  params.running_standalone = false;
-  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 = base::FeatureList::IsEnabled(features::kMash);
-  return std::make_unique<ui::Service>(params);
-}
-
-void RegisterUIServiceInProcessIfNecessary(
-    ServiceManagerConnection* connection) {
-  // Some tests don't create BrowserMainLoop.
-  if (!BrowserMainLoop::GetInstance())
-    return;
-  // Do not embed the UI service when running in mash.
-  if (base::FeatureList::IsEnabled(features::kMash))
-    return;
-  // Do not embed the UI service if not running with mus.
-  if (!features::IsMusEnabled())
-    return;
-
-  service_manager::EmbeddedServiceInfo info;
-  info.factory = base::Bind(
-      &CreateEmbeddedUIService, base::ThreadTaskRunnerHandle::Get(),
-      BrowserMainLoop::GetInstance()->image_cursors_set()->GetWeakPtr(),
-      GetDiscardableSharedMemoryManager());
-  info.use_own_thread = true;
-  info.message_loop_type = base::MessageLoop::TYPE_UI;
-  info.thread_priority = base::ThreadPriority::DISPLAY;
-  connection->AddEmbeddedService(ui::mojom::kServiceName, info);
-}
-#endif
-
 std::unique_ptr<service_manager::Service> CreateNetworkService() {
   // The test interface doesn't need to be implemented in the in-process case.
   auto registry = std::make_unique<service_manager::BinderRegistry>();
@@ -548,10 +502,6 @@
                                                       entry.second);
   }
 
-#if BUILDFLAG(ENABLE_MUS)
-  RegisterUIServiceInProcessIfNecessary(packaged_services_connection_.get());
-#endif
-
   // This is safe to assign directly from any thread, because
   // ServiceManagerContext must be constructed before anyone can call
   // GetConnectorForIOThread().
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 5468b8e..65b35e07 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -17,6 +17,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/syslog_logging.h"
 #include "content/browser/background_fetch/background_fetch_context.h"
 #include "content/browser/blob_storage/blob_registry_wrapper.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
@@ -831,6 +832,7 @@
   int process_id = bindings_.dispatch_context();
   if (!ChildProcessSecurityPolicy::GetInstance()->CanAccessDataForOrigin(
           process_id, origin.GetURL())) {
+    SYSLOG(WARNING) << "Killing renderer: illegal localStorage request.";
     bindings_.ReportBadMessage("Access denied for localStorage request");
     return;
   }
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 85aff036..30d546d 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -22,6 +22,7 @@
 #include "content/browser/accessibility/browser_accessibility_android.h"
 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
 #include "content/browser/android/content_view_core.h"
+#include "content/browser/android/interstitial_page_delegate_android.h"
 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/media/android/browser_media_player_manager.h"
@@ -41,8 +42,9 @@
 #include "content/public/common/content_switches.h"
 #include "jni/WebContentsImpl_jni.h"
 #include "net/android/network_library.h"
+#include "ui/accessibility/ax_assistant_structure.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
+#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
 #include "ui/android/overscroll_refresh_handler.h"
 #include "ui/android/window_android.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -90,7 +92,8 @@
 
 ScopedJavaLocalRef<jobject> JNI_WebContentsImpl_CreateJavaAXSnapshot(
     JNIEnv* env,
-    const ui::AXSnapshotNodeAndroid* node,
+    const ui::AssistantTree* tree,
+    const ui::AssistantNode* node,
     bool is_root) {
   ScopedJavaLocalRef<jstring> j_text =
       ConvertUTF16ToJavaString(env, node->text);
@@ -103,15 +106,16 @@
           node->text_size, node->bold, node->italic, node->underline,
           node->line_through, j_class);
 
-  if (node->has_selection) {
+  if (node->selection.has_value()) {
     Java_WebContentsImpl_setAccessibilitySnapshotSelection(
-        env, j_node, node->start_selection, node->end_selection);
+        env, j_node, node->selection->start(), node->selection->end());
   }
 
-  for (auto& child : node->children) {
+  for (int child : node->children_indices) {
     Java_WebContentsImpl_addAccessibilityNodeAsChild(
         env, j_node,
-        JNI_WebContentsImpl_CreateJavaAXSnapshot(env, child.get(), false));
+        JNI_WebContentsImpl_CreateJavaAXSnapshot(
+            env, tree, tree->nodes[child].get(), false));
   }
   return j_node;
 }
@@ -127,10 +131,10 @@
   std::unique_ptr<BrowserAccessibilityManagerAndroid> manager(
       static_cast<BrowserAccessibilityManagerAndroid*>(
           BrowserAccessibilityManager::Create(result, nullptr)));
-  auto snapshot = ui::AXSnapshotNodeAndroid::Create(
-      result, manager->ShouldExposePasswordText());
-  ScopedJavaLocalRef<jobject> j_root =
-      JNI_WebContentsImpl_CreateJavaAXSnapshot(env, snapshot.get(), true);
+  std::unique_ptr<ui::AssistantTree> assistant_tree =
+      ui::CreateAssistantTree(result, manager->ShouldExposePasswordText());
+  ScopedJavaLocalRef<jobject> j_root = JNI_WebContentsImpl_CreateJavaAXSnapshot(
+      env, assistant_tree.get(), assistant_tree->nodes.front().get(), true);
   Java_WebContentsImpl_onAccessibilitySnapshot(env, j_root, callback);
 }
 
@@ -420,6 +424,19 @@
   web_contents_->SetAudioMuted(mute);
 }
 
+void WebContentsAndroid::ShowInterstitialPage(JNIEnv* env,
+                                              const JavaParamRef<jobject>& obj,
+                                              const JavaParamRef<jstring>& jurl,
+                                              jlong delegate_ptr) {
+  GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
+  InterstitialPageDelegateAndroid* delegate =
+      reinterpret_cast<InterstitialPageDelegateAndroid*>(delegate_ptr);
+  InterstitialPage* interstitial = InterstitialPage::Create(
+      web_contents_, false, url, delegate);
+  delegate->set_interstitial_page(interstitial);
+  interstitial->Show();
+}
+
 jboolean WebContentsAndroid::IsShowingInterstitialPage(
     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 04455bd0..52b0769 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -100,6 +100,10 @@
                      const base::android::JavaParamRef<jobject>& jobj,
                      jboolean mute);
 
+  void ShowInterstitialPage(JNIEnv* env,
+                            const base::android::JavaParamRef<jobject>& obj,
+                            const base::android::JavaParamRef<jstring>& jurl,
+                            jlong delegate_ptr);
   jboolean IsShowingInterstitialPage(
       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 189b3f0..6a9a264 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -60,6 +60,7 @@
 #include "content/browser/loader/loader_io_thread_notifier.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/manifest/manifest_manager_host.h"
+#include "content/browser/media/audio_stream_broker.h"
 #include "content/browser/media/audio_stream_monitor.h"
 #include "content/browser/media/capture/web_contents_audio_muter.h"
 #include "content/browser/media/media_web_contents_observer.h"
@@ -6158,6 +6159,19 @@
   view_size_before_emulation_ = gfx::Size();
 }
 
+ForwardingAudioStreamFactory* WebContentsImpl::GetAudioStreamFactory() {
+  if (!audio_stream_factory_) {
+    audio_stream_factory_.emplace(
+        this,
+        content::ServiceManagerConnection::GetForProcess()
+            ->GetConnector()
+            ->Clone(),
+        AudioStreamBrokerFactory::CreateImpl());
+  }
+
+  return &*audio_stream_factory_;
+}
+
 void WebContentsImpl::MediaStartedPlaying(
     const WebContentsObserver::MediaPlayerInfo& media_info,
     const WebContentsObserver::MediaPlayerId& id) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index a5cfbff9..730b9d3 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -19,6 +19,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "base/process/process.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -33,6 +34,7 @@
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/frame_host/render_frame_host_manager.h"
 #include "content/browser/media/audio_stream_monitor.h"
+#include "content/browser/media/forwarding_audio_stream_factory.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
@@ -871,6 +873,8 @@
     return &audio_stream_monitor_;
   }
 
+  ForwardingAudioStreamFactory* GetAudioStreamFactory();
+
   // Called by MediaWebContentsObserver when playback starts or stops.  See the
   // WebContentsObserver function stubs for more details.
   void MediaStartedPlaying(
@@ -1649,6 +1653,9 @@
   // Monitors power levels for audio streams associated with this WebContents.
   AudioStreamMonitor audio_stream_monitor_;
 
+  // Coordinates all the audio streams for this WebContents. Lazily initialized.
+  base::Optional<ForwardingAudioStreamFactory> audio_stream_factory_;
+
   // Created on-demand to mute all audio output from this WebContents.
   std::unique_ptr<WebContentsAudioMuter> audio_muter_;
 
diff --git a/content/browser/web_package/signed_exchange_header_parser.cc b/content/browser/web_package/signed_exchange_header_parser.cc
index cac1f6d..5a9c66a 100644
--- a/content/browser/web_package/signed_exchange_header_parser.cc
+++ b/content/browser/web_package/signed_exchange_header_parser.cc
@@ -238,7 +238,7 @@
           "'certUrl' parameter is not a valid URL.");
       return base::nullopt;
     }
-    const std::string cert_sha256_string = value.params["certSha256"];
+    const std::string cert_sha256_string = value.params[kCertSha256Key];
     if (cert_sha256_string.size() != crypto::kSHA256Length) {
       // TODO(https://crbug.com/819467) : When we will support "ed25519Key", the
       // params may not have "certSha256".
@@ -252,7 +252,7 @@
     sig.cert_sha256 = std::move(cert_sha256);
     // TODO(https://crbug.com/819467): Support ed25519key.
     // sig.ed25519_key = value.params["ed25519Key"];
-    sig.validity_url = GURL(value.params["validityUrl"]);
+    sig.validity_url = GURL(value.params[kValidityUrlKey]);
     if (!sig.validity_url.is_valid()) {
       signed_exchange_utils::ReportErrorAndEndTraceEvent(
           devtools_proxy, "SignedExchangeHeaderParser::ParseSignature",
@@ -265,13 +265,13 @@
           "'validityUrl' parameter can't have a fragment.");
       return base::nullopt;
     }
-    if (!base::StringToUint64(value.params["date"], &sig.date)) {
+    if (!base::StringToUint64(value.params[kDateKey], &sig.date)) {
       signed_exchange_utils::ReportErrorAndEndTraceEvent(
           devtools_proxy, "SignedExchangeHeaderParser::ParseSignature",
           "'date' parameter is not a number.");
       return base::nullopt;
     }
-    if (!base::StringToUint64(value.params["expires"], &sig.expires)) {
+    if (!base::StringToUint64(value.params[kExpiresKey], &sig.expires)) {
       signed_exchange_utils::ReportErrorAndEndTraceEvent(
           devtools_proxy, "SignedExchangeHeaderParser::ParseSignature",
           "'expires' parameter is not a number.");
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index ab6ccbba4..5dad37a 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -43,6 +43,15 @@
                                         std::move(update));
 }
 
+namespace {
+
+std::string CleanUpPath(const std::string& path) {
+  // Remove the query string for named resource lookups.
+  return path.substr(0, path.find_first_of('?'));
+}
+
+}  // namespace
+
 // Internal class to hide the fact that WebUIDataSourceImpl implements
 // URLDataSource.
 class WebUIDataSourceImpl::InternalDataSource : public URLDataSource {
@@ -88,8 +97,7 @@
     return parent_->deny_xframe_options_;
   }
   bool IsGzipped(const std::string& path) const override {
-    return parent_->use_gzip_ &&
-        parent_->excluded_paths_.find(path) == parent_->excluded_paths_.end();
+    return parent_->IsGzipped(path);
   }
 
  private:
@@ -275,8 +283,7 @@
   int resource_id = default_resource_;
   std::map<std::string, int>::iterator result;
   // Remove the query string for named resource lookups.
-  std::string file_path = path.substr(0, path.find_first_of('?'));
-  result = path_to_idr_map_.find(file_path);
+  result = path_to_idr_map_.find(CleanUpPath(path));
   if (result != path_to_idr_map_.end())
     resource_id = result->second;
   DCHECK_NE(resource_id, -1);
@@ -292,4 +299,8 @@
   callback.Run(base::RefCountedString::TakeString(&template_data));
 }
 
+bool WebUIDataSourceImpl::IsGzipped(const std::string& path) const {
+  return use_gzip_ && excluded_paths_.count(CleanUpPath(path)) == 0;
+}
+
 }  // namespace content
diff --git a/content/browser/webui/web_ui_data_source_impl.h b/content/browser/webui/web_ui_data_source_impl.h
index 18c4875..06c1bd9 100644
--- a/content/browser/webui/web_ui_data_source_impl.h
+++ b/content/browser/webui/web_ui_data_source_impl.h
@@ -13,6 +13,7 @@
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/values.h"
 #include "content/browser/webui/url_data_manager.h"
@@ -69,6 +70,8 @@
   friend class WebUIDataSource;
   friend class WebUIDataSourceTest;
 
+  FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzipped);
+
   explicit WebUIDataSourceImpl(const std::string& source_name);
 
   // Methods that match URLDataSource which are called by
@@ -85,6 +88,8 @@
     add_load_time_data_defaults_ = false;
   }
 
+  bool IsGzipped(const std::string& path) const;
+
   // The name of this source.
   // E.g., for favicons, this could be "favicon", which results in paths for
   // specific resources like "favicon/34" getting sent to this source.
diff --git a/content/browser/webui/web_ui_data_source_unittest.cc b/content/browser/webui/web_ui_data_source_unittest.cc
index f2974ff..6125eb9 100644
--- a/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/content/browser/webui/web_ui_data_source_unittest.cc
@@ -212,4 +212,24 @@
   EXPECT_EQ(GetMimeType("foo.js?abc?abc"), js);
 }
 
+TEST_F(WebUIDataSourceTest, IsGzipped) {
+  EXPECT_FALSE(source()->IsGzipped("foobar"));
+
+  source()->AddResourcePath("foobar", kDummyResourceId);
+  source()->SetDefaultResource(kDummyDefaultResourceId);
+  source()->SetJsonPath("strings.js");
+  source()->UseGzip({"json/special/path"});
+
+  EXPECT_TRUE(source()->IsGzipped("foobar"));
+  EXPECT_TRUE(source()->IsGzipped("foobar?query"));
+
+  EXPECT_TRUE(source()->IsGzipped("unknown_path"));
+  EXPECT_TRUE(source()->IsGzipped("unknown_path?query"));
+
+  EXPECT_FALSE(source()->IsGzipped("json/special/path"));
+  EXPECT_FALSE(source()->IsGzipped("json/special/path?query"));
+  EXPECT_FALSE(source()->IsGzipped("strings.js"));
+  EXPECT_FALSE(source()->IsGzipped("strings.js?query"));
+}
+
 }  // namespace content
diff --git a/content/common/OWNERS b/content/common/OWNERS
index 5baee777..c3390635 100644
--- a/content/common/OWNERS
+++ b/content/common/OWNERS
@@ -7,10 +7,14 @@
 per-file sandbox_init_win.cc=file://sandbox/win/OWNERS
 
 # Mac Sandbox.
-per-file sandbox_init_mac.*=rsesek@chromium.org
-per-file sandbox_mac*=rsesek@chromium.org
+per-file sandbox_init_mac.cc=set noparent
+per-file sandbox_init_mac.cc=file://sandbox/mac/OWNERS
+
+per-file sandbox_mac*=set noparent
+per-file sandbox_mac*=file://sandbox/mac/OWNERS
+
 per-file *.sb=set noparent
-per-file *.sb=rsesek@chromium.org
+per-file *.sb=file://sandbox/mac/OWNERS
 
 per-file pepper*=bauerb@chromium.org
 per-file plugin*=bauerb@chromium.org
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index f61f24dd..1b5b3ad3 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -1095,11 +1095,6 @@
                     base::string16 /* search_text */,
                     blink::WebFindOptions)
 
-// This message notifies the frame that it is no longer the active frame in the
-// current find session, and so it should clear its active find match (and no
-// longer highlight it with special coloring).
-IPC_MESSAGE_ROUTED0(FrameMsg_ClearActiveFindMatch)
-
 // This message notifies the frame that the user has closed the find-in-page
 // window (and what action to take regarding the selection).
 IPC_MESSAGE_ROUTED1(FrameMsg_StopFinding, content::StopFindAction /* action */)
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 2ee1e8c..bb0abafb 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -115,7 +115,7 @@
   // allocator.
   if (!command_line.HasSwitch(switches::kSingleProcess)) {
     discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-    if (features::IsMusEnabled()) {
+    if (features::IsMashEnabled()) {
 #if defined(USE_AURA)
       GetServiceManagerConnection()->GetConnector()->BindInterface(
           ui::mojom::kServiceName, &manager_ptr);
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index f21c02e..43f1a6e 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -129,6 +129,7 @@
     "java/src/org/chromium/content/browser/GestureListenerManagerImpl.java",
     "java/src/org/chromium/content/browser/GpuProcessCallback.java",
     "java/src/org/chromium/content/browser/InterfaceRegistrarImpl.java",
+    "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java",
     "java/src/org/chromium/content/browser/JavascriptInjectorImpl.java",
     "java/src/org/chromium/content/browser/JavascriptInterface.java",
     "java/src/org/chromium/content/browser/JoystickHandler.java",
@@ -364,6 +365,7 @@
     "java/src/org/chromium/content/browser/GestureListenerManagerImpl.java",
     "java/src/org/chromium/content/browser/GpuProcessCallback.java",
     "java/src/org/chromium/content/browser/InterfaceRegistrarImpl.java",
+    "java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java",
     "java/src/org/chromium/content/browser/JavascriptInjectorImpl.java",
     "java/src/org/chromium/content/browser/LauncherThread.java",
     "java/src/org/chromium/content/browser/MediaSessionImpl.java",
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 246cbf6..ae520378ef 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
@@ -172,8 +172,7 @@
     }
 
     private void hidePopupsAndClearSelection() {
-        getSelectionPopupController().destroyActionModeAndUnselect();
-        mWebContents.dismissTextHandles();
+        getSelectionPopupController().clearSelection();
         PopupController.hideAll(mWebContents);
     }
 
@@ -266,10 +265,6 @@
                 hidePopupsAndPreserveSelection();
             } else {
                 hidePopupsAndClearSelection();
-                // Clear the selection. The selection is cleared on destroying IME
-                // and also here since we may receive destroy first, for example
-                // when focus is lost in webview.
-                controller.clearSelection();
             }
         }
         if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, mHasInputFocus);
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/InterstitialPageDelegateAndroid.java b/content/public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java
similarity index 71%
rename from content/public/test/android/javatests/src/org/chromium/content/browser/test/InterstitialPageDelegateAndroid.java
rename to content/public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java
index b952f78..9af0cd5 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/InterstitialPageDelegateAndroid.java
+++ b/content/public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.content.browser.test;
+package org.chromium.content.browser;
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
-import org.chromium.content_public.browser.WebContents;
 
 /**
  * Allows the specification and handling of Interstitial pages in java.
  */
 @JNINamespace("content")
 public class InterstitialPageDelegateAndroid {
+
     private long mNativePtr;
 
     /**
@@ -27,16 +27,26 @@
     }
 
     /**
+     * @return The pointer to the underlying native counterpart.
+     */
+    @VisibleForTesting
+    public long getNative() {
+        return mNativePtr;
+    }
+
+    /**
      * Called when "proceed" is triggered on the interstitial.
      */
     @CalledByNative
-    protected void onProceed() {}
+    protected void onProceed() {
+    }
 
     /**
      * Called when "dont' proceed" is triggered on the interstitial.
      */
     @CalledByNative
-    protected void onDontProceed() {}
+    protected void onDontProceed() {
+    }
 
     /**
      * Called when a command has been received from the interstitial.
@@ -44,7 +54,8 @@
      * @param command The command that was received.
      */
     @CalledByNative
-    protected void commandReceived(String command) {}
+    protected void commandReceived(String command) {
+    }
 
     @CalledByNative
     private void onNativeDestroyed() {
@@ -65,19 +76,7 @@
         if (mNativePtr != 0) nativeDontProceed(mNativePtr);
     }
 
-    /**
-     * Shows an interstitial page driven by this delegate.
-     *
-     * @param url The URL being blocked by the interstitial.
-     * @param webContents The {@link WebContents} the interstitial to show on.
-     */
-    public void showInterstitialPage(String url, WebContents webContents) {
-        if (mNativePtr != 0) nativeShowInterstitialPage(mNativePtr, url, webContents);
-    }
-
     private native long nativeInit(String htmlContent);
     private native void nativeProceed(long nativeInterstitialPageDelegateAndroid);
     private native void nativeDontProceed(long nativeInterstitialPageDelegateAndroid);
-    private native void nativeShowInterstitialPage(
-            long nativeInterstitialPageDelegateAndroid, String url, WebContents webContents);
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/PopupController.java b/content/public/android/java/src/org/chromium/content/browser/PopupController.java
index caaad3bb..27ca475 100644
--- a/content/public/android/java/src/org/chromium/content/browser/PopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/PopupController.java
@@ -5,7 +5,6 @@
 package org.chromium.content.browser;
 
 import org.chromium.content.browser.selection.SelectionPopupControllerImpl;
-import org.chromium.content.browser.webcontents.WebContentsImpl;
 import org.chromium.content.browser.webcontents.WebContentsUserData;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContents.UserDataFactory;
@@ -57,7 +56,6 @@
         SelectionPopupControllerImpl controller =
                 SelectionPopupControllerImpl.fromWebContents(webContents);
         if (controller != null) controller.destroyActionModeAndUnselect();
-        ((WebContentsImpl) webContents).dismissTextHandles();
         PopupController.hideAll(webContents);
     }
 
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 2fa7ac0..d566d42 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
@@ -921,7 +921,6 @@
     public void onDestroyActionMode() {
         mActionMode = null;
         if (mUnselectAllOnDismiss) {
-            mWebContents.dismissTextHandles();
             clearSelection();
         }
     }
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 1cc500f..edd12fd 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
@@ -429,6 +429,12 @@
     }
 
     @Override
+    public void showInterstitialPage(
+            String url, long interstitialPageDelegateAndroid) {
+        nativeShowInterstitialPage(mNativeWebContentsAndroid, url, interstitialPageDelegateAndroid);
+    }
+
+    @Override
     public boolean isShowingInterstitialPage() {
         return nativeIsShowingInterstitialPage(mNativeWebContentsAndroid);
     }
@@ -870,6 +876,8 @@
     private native void nativeSuspendAllMediaPlayers(long nativeWebContentsAndroid);
     private native void nativeSetAudioMuted(long nativeWebContentsAndroid, boolean mute);
     private native int nativeGetBackgroundColor(long nativeWebContentsAndroid);
+    private native void nativeShowInterstitialPage(long nativeWebContentsAndroid,
+            String url, long nativeInterstitialPageDelegateAndroid);
     private native boolean nativeIsShowingInterstitialPage(long nativeWebContentsAndroid);
     private native boolean nativeFocusLocationBarByDefault(long nativeWebContentsAndroid);
     private native boolean nativeIsRenderWidgetHostViewReady(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 45dcf837..094a9b9 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
@@ -211,6 +211,16 @@
     int getBackgroundColor();
 
     /**
+     * Shows an interstitial page driven by the passed in delegate.
+     *
+     * @param url The URL being blocked by the interstitial.
+     * @param interstitialPageDelegateAndroid The delegate handling the interstitial.
+     */
+    @VisibleForTesting
+    void showInterstitialPage(
+            String url, long interstitialPageDelegateAndroid);
+
+    /**
      * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
      */
     boolean isShowingInterstitialPage();
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java b/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
index 60d06dc..e0800ff 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
@@ -17,7 +17,6 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
-import org.chromium.content.browser.test.InterstitialPageDelegateAndroid;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 import org.chromium.content.browser.test.util.TouchCommon;
@@ -118,7 +117,8 @@
                 new Callable<TestWebContentsObserver>() {
                     @Override
                     public TestWebContentsObserver call() throws Exception {
-                        delegate.showInterstitialPage(URL, mActivityTestRule.getWebContents());
+                        mActivityTestRule.getWebContents().showInterstitialPage(
+                                URL, delegate.getNative());
                         return new TestWebContentsObserver(mActivityTestRule.getWebContents());
                     }
                 });
diff --git a/content/public/browser/android/compositor_client.h b/content/public/browser/android/compositor_client.h
index bb03a251..38257521 100644
--- a/content/public/browser/android/compositor_client.h
+++ b/content/public/browser/android/compositor_client.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "content/common/content_export.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace content {
 
@@ -21,7 +22,7 @@
   virtual void DidSwapFrame(int pending_frames) {}
 
   // This is called on all swap buffers, regardless of cause.
-  virtual void DidSwapBuffers() {}
+  virtual void DidSwapBuffers(const gfx::Size& swap_size) {}
 
  protected:
   CompositorClient() {}
diff --git a/content/public/browser/desktop_capture.cc b/content/public/browser/desktop_capture.cc
index 016a774..2d41880 100644
--- a/content/public/browser/desktop_capture.cc
+++ b/content/public/browser/desktop_capture.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "content/public/common/content_features.h"
 
 namespace content {
 namespace desktop_capture {
@@ -24,7 +25,11 @@
   } else {
     options.set_allow_use_magnification_api(true);
   }
-#endif  // defined(OS_WIN)
+#elif defined(OS_MACOSX)
+  if (base::FeatureList::IsEnabled(features::kIOSurfaceCapturer)) {
+    options.set_allow_iosurface(true);
+  }
+#endif
   return options;
 }
 
diff --git a/content/public/browser/overlay_window.h b/content/public/browser/overlay_window.h
index 96c468a..553fc06 100644
--- a/content/public/browser/overlay_window.h
+++ b/content/public/browser/overlay_window.h
@@ -48,6 +48,7 @@
 
   // Retrieve the ui::Layers corresponding to the video and controls.
   virtual ui::Layer* GetVideoLayer() = 0;
+  virtual ui::Layer* GetControlsBackgroundLayer() = 0;
   virtual ui::Layer* GetCloseControlsLayer() = 0;
   virtual ui::Layer* GetPlayPauseControlsLayer() = 0;
 
diff --git a/content/public/browser/overscroll_configuration.h b/content/public/browser/overscroll_configuration.h
index 1cfca76..9df88576 100644
--- a/content/public/browser/overscroll_configuration.h
+++ b/content/public/browser/overscroll_configuration.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_OVERSCROLL_CONFIGURATION_H_
 
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "content/common/content_export.h"
 
 namespace content {
@@ -62,6 +63,8 @@
 
   static bool TouchpadOverscrollHistoryNavigationEnabled();
 
+  static base::TimeDelta MaxInertialEventsBeforeOverscrollCancellation();
+
  private:
   friend class ScopedHistoryNavigationMode;
   friend class ScopedPullToRefreshMode;
diff --git a/content/public/browser/render_frame_metadata_provider.h b/content/public/browser/render_frame_metadata_provider.h
index 25b86d3..6ee7068f 100644
--- a/content/public/browser/render_frame_metadata_provider.h
+++ b/content/public/browser/render_frame_metadata_provider.h
@@ -33,10 +33,9 @@
     virtual void OnRenderFrameSubmission() = 0;
 
     // Called to indicate that the viz::LocalSurfaceId within the
-    // RenderFrameMetadata has changed. For production builds, this means that
-    // the child_sequence_number of the viz::LocalSurfaceId has changed. For
-    // unit tests, this means that any part of the viz::LocalSurfaceId has
-    // changed.
+    // RenderFrameMetadata has changed. Note that this is called as
+    // soon as |metadata| arrives and does not wait for the frame token
+    // to pass in Viz.
     virtual void OnLocalSurfaceIdChanged(
         const cc::RenderFrameMetadata& metadata) = 0;
   };
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index f3300dd..5c9bb09 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -239,7 +239,7 @@
 
 // Off-main-thread WebSocket. See https://crbug.com/825740
 const base::Feature kOffMainThreadWebSocket{"OffMainThreadWebSocket",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Origin Manifest. See crbug.com/751996
 const base::Feature kOriginManifest{"OriginManifest",
@@ -619,6 +619,10 @@
 const base::Feature kDeviceMonitorMac{"DeviceMonitorMac",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enable IOSurface based screen capturer.
+const base::Feature kIOSurfaceCapturer{"IOSurfaceCapturer",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
 // The V2 sandbox on MacOS removes the unsandboed warmup phase and sandboxes the
 // entire life of the process.
 const base::Feature kMacV2Sandbox{"MacV2Sandbox",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 293dad7e..c328fb68 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -148,6 +148,7 @@
 
 #if defined(OS_MACOSX)
 CONTENT_EXPORT extern const base::Feature kDeviceMonitorMac;
+CONTENT_EXPORT extern const base::Feature kIOSurfaceCapturer;
 CONTENT_EXPORT extern const base::Feature kMacV2Sandbox;
 #endif  // defined(OS_MACOSX)
 
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 08de4404..beebfd7 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -33,7 +33,6 @@
     "javatests/src/org/chromium/content/browser/test/ChildProcessAllocatorSettings.java",
     "javatests/src/org/chromium/content/browser/test/ChildProcessAllocatorSettingsHook.java",
     "javatests/src/org/chromium/content/browser/test/ContentJUnit4ClassRunner.java",
-    "javatests/src/org/chromium/content/browser/test/InterstitialPageDelegateAndroid.java",
     "javatests/src/org/chromium/content/browser/test/NativeLibraryTestRule.java",
     "javatests/src/org/chromium/content/browser/test/mock/MockRenderFrameHost.java",
     "javatests/src/org/chromium/content/browser/test/mock/MockWebContents.java",
@@ -57,26 +56,3 @@
     "javatests/src/org/chromium/content/browser/test/util/UiUtils.java",
   ]
 }
-
-generate_jni("content_test_jni") {
-  testonly = true
-  jni_package = "content/public/test"
-  sources = [
-    "javatests/src/org/chromium/content/browser/test/InterstitialPageDelegateAndroid.java",
-  ]
-}
-
-static_library("content_native_test_support") {
-  testonly = true
-  sources = [
-    "interstitial_page_delegate_android.cc",
-    "interstitial_page_delegate_android.h",
-  ]
-  deps = [
-    ":content_test_jni",
-    "//base",
-    "//content/public/browser",
-    "//device/gamepad",
-    "//media/midi",
-  ]
-}
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 e12e265..afbd264 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
@@ -127,6 +127,9 @@
     }
 
     @Override
+    public void showInterstitialPage(String url, long interstitialPageDelegateAndroid) {}
+
+    @Override
     public boolean isShowingInterstitialPage() {
         return false;
     }
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 03bd1e4db..8bb6e40 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -334,7 +334,7 @@
     // When no accessibility events are in-flight post a task to send
     // the events to the browser. We use PostTask so that we can queue
     // up additional events.
-    render_frame_->GetTaskRunner(blink::TaskType::kInternalAccessibility)
+    render_frame_->GetTaskRunner(blink::TaskType::kInternalDefault)
         ->PostTask(FROM_HERE,
                    base::BindOnce(
                        &RenderAccessibilityImpl::SendPendingAccessibilityEvents,
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index e2cf1e3..b0b24bc 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -315,7 +315,7 @@
     sent_visual_properties_ = pending_visual_properties_;
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled() && mus_embedded_frame_) {
+  if (features::IsMashEnabled() && mus_embedded_frame_) {
     mus_embedded_frame_->SetWindowBounds(GetLocalSurfaceId(),
                                          FrameRectInPixels());
   }
diff --git a/content/renderer/media/media_interface_factory.cc b/content/renderer/media/media_interface_factory.cc
index 77c7287..f457c38 100644
--- a/content/renderer/media/media_interface_factory.cc
+++ b/content/renderer/media/media_interface_factory.cc
@@ -82,6 +82,20 @@
   GetMediaInterfaceFactory()->CreateCdm(key_system, std::move(request));
 }
 
+void MediaInterfaceFactory::CreateDecryptor(
+    int cdm_id,
+    media::mojom::DecryptorRequest request) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&MediaInterfaceFactory::CreateDecryptor,
+                                  weak_this_, cdm_id, std::move(request)));
+    return;
+  }
+
+  DVLOG(1) << __func__;
+  GetMediaInterfaceFactory()->CreateDecryptor(cdm_id, std::move(request));
+}
+
 void MediaInterfaceFactory::CreateCdmProxy(
     const std::string& cdm_guid,
     media::mojom::CdmProxyRequest request) {
diff --git a/content/renderer/media/media_interface_factory.h b/content/renderer/media/media_interface_factory.h
index ee4a33d..54f8ab77 100644
--- a/content/renderer/media/media_interface_factory.h
+++ b/content/renderer/media/media_interface_factory.h
@@ -37,6 +37,8 @@
                       media::mojom::RendererRequest request) final;
   void CreateCdm(const std::string& key_system,
                  media::mojom::ContentDecryptionModuleRequest request) final;
+  void CreateDecryptor(int cdm_id,
+                       media::mojom::DecryptorRequest request) final;
   // TODO(xhwang): We should not expose this here.
   void CreateCdmProxy(const std::string& cdm_guid,
                       media::mojom::CdmProxyRequest request) final;
diff --git a/content/renderer/media/webrtc/rtc_video_decoder.cc b/content/renderer/media/webrtc/rtc_video_decoder.cc
index e82cf263..238e3a9d 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder.cc
@@ -159,7 +159,6 @@
 int32_t RTCVideoDecoder::Decode(
     const webrtc::EncodedImage& inputImage,
     bool missingFrames,
-    const webrtc::RTPFragmentationHeader* /*fragmentation*/,
     const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/,
     int64_t /*renderTimeMs*/) {
   DVLOG(3) << "Decode";
@@ -470,8 +469,8 @@
     frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY,
                                   picture.allow_overlay());
 #if defined(OS_ANDROID)
-    frame->metadata()->SetBoolean(media::VideoFrameMetadata::SURFACE_TEXTURE,
-                                  picture.surface_texture());
+    frame->metadata()->SetBoolean(media::VideoFrameMetadata::TEXTURE_OWNER,
+                                  picture.texture_owner());
     frame->metadata()->SetBoolean(
         media::VideoFrameMetadata::WANTS_PROMOTION_HINT,
         picture.wants_promotion_hint());
diff --git a/content/renderer/media/webrtc/rtc_video_decoder.h b/content/renderer/media/webrtc/rtc_video_decoder.h
index 8a29b580..4db50b3 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder.h
+++ b/content/renderer/media/webrtc/rtc_video_decoder.h
@@ -70,9 +70,8 @@
   // Called on WebRTC DecodingThread.
   int32_t Decode(const webrtc::EncodedImage& inputImage,
                  bool missingFrames,
-                 const webrtc::RTPFragmentationHeader* fragmentation,
-                 const webrtc::CodecSpecificInfo* codecSpecificInfo = NULL,
-                 int64_t renderTimeMs = -1) override;
+                 const webrtc::CodecSpecificInfo* codecSpecificInfo,
+                 int64_t renderTimeMs) override;
   // Called on WebRTC DecodingThread.
   int32_t RegisterDecodeCompleteCallback(
       webrtc::DecodedImageCallback* callback) override;
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc b/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
index cd52471..050c9f6 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
@@ -208,7 +208,7 @@
   CreateDecoder(webrtc::kVideoCodecVP8);
   webrtc::EncodedImage input_image;
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
 }
 
 TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnIncompleteFrame) {
@@ -217,7 +217,7 @@
   webrtc::EncodedImage input_image;
   input_image._completeFrame = false;
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
 }
 
 TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnMissingFrames) {
@@ -228,7 +228,7 @@
   bool missingFrames = true;
   EXPECT_EQ(
       WEBRTC_VIDEO_CODEC_ERROR,
-      rtc_decoder_->Decode(input_image, missingFrames, nullptr, nullptr, 0));
+      rtc_decoder_->Decode(input_image, missingFrames, nullptr, 0));
 }
 
 TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) {
@@ -336,7 +336,7 @@
   input_image._frameType = webrtc::kVideoFrameDelta;
   input_image._length = kMinResolutionWidth * kMaxResolutionHeight;
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
   RunUntilIdle();
 
   // Notify the decoder about a platform error.
@@ -347,13 +347,13 @@
   // Expect decode call to reset decoder, and set up a new VDA to track it.
   SetUpResetVDA();
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
   EXPECT_EQ(1, rtc_decoder_->GetVDAErrorCounterForTesting());
 
   // Decoder expects a frame with size after reset, so drops any other frames.
   // However, we should still increment the error counter.
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
   EXPECT_EQ(2, rtc_decoder_->GetVDAErrorCounterForTesting());
 }
 
@@ -379,7 +379,7 @@
   uint32_t i = 0;
   while (i++ < kMaxNumDecodeRequests) {
     const int32_t result =
-        rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0);
+        rtc_decoder_->Decode(input_image, false, nullptr, 0);
     RunUntilIdle();
     if (result == WEBRTC_VIDEO_CODEC_OK)
       EXPECT_EQ(0, rtc_decoder_->GetVDAErrorCounterForTesting());
@@ -411,7 +411,7 @@
   input_image._length = sizeof(buffer);
   EXPECT_CALL(*mock_vda_, Decode(_)).Times(1);
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
   RunUntilIdle();
 
   // InitDecode and Decode after Release should succeed.
@@ -419,7 +419,7 @@
   rtc_decoder_->Release();
   Initialize();
   EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
-            rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
+            rtc_decoder_->Decode(input_image, false, nullptr, 0));
 }
 
 INSTANTIATE_TEST_CASE_P(CodecProfiles,
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 9143e53..78e689e 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -32,7 +32,7 @@
 
 // static
 void RendererWindowTreeClient::CreateIfNecessary(int routing_id) {
-  if (!features::IsMusEnabled() || Get(routing_id))
+  if (!features::IsMashEnabled() || Get(routing_id))
     return;
   RendererWindowTreeClient* connection =
       new RendererWindowTreeClient(routing_id);
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index beff6e0..cba2c9cc 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1751,7 +1751,6 @@
                         OnGetSerializedHtmlWithLocalLinks)
     IPC_MESSAGE_HANDLER(FrameMsg_SerializeAsMHTML, OnSerializeAsMHTML)
     IPC_MESSAGE_HANDLER(FrameMsg_Find, OnFind)
-    IPC_MESSAGE_HANDLER(FrameMsg_ClearActiveFindMatch, OnClearActiveFindMatch)
     IPC_MESSAGE_HANDLER(FrameMsg_StopFinding, OnStopFinding)
     IPC_MESSAGE_HANDLER(FrameMsg_EnableViewSourceMode, OnEnableViewSourceMode)
     IPC_MESSAGE_HANDLER(FrameMsg_SuppressFurtherDialogs,
@@ -6167,11 +6166,6 @@
   frame_->RequestFind(request_id, WebString::FromUTF16(search_text), options);
 }
 
-void RenderFrameImpl::OnClearActiveFindMatch() {
-  frame_->ExecuteCommand(WebString::FromUTF8("CollapseSelection"));
-  frame_->ClearActiveFindMatch();
-}
-
 #define STATIC_ASSERT_ENUM(a, b)                            \
   static_assert(static_cast<int>(a) == static_cast<int>(b), \
                 "mismatching enums: " #a)
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 6d3deeb..51a5f1b 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1044,7 +1044,6 @@
   void OnFind(int request_id,
               const base::string16& search_text,
               const blink::WebFindOptions& options);
-  void OnClearActiveFindMatch();
   void OnStopFinding(StopFindAction action);
   void OnEnableViewSourceMode();
   void OnSuppressFurtherDialogs();
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index a355de34..5dbe0ff 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -243,7 +243,7 @@
       render_widget_->GetOriginalScreenInfo();
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
     RendererWindowTreeClient* renderer_window_tree_client =
         RendererWindowTreeClient::Get(render_widget_->routing_id());
     // It's possible a MusEmbeddedFrame has already been scheduled for creation
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0bbf5675..03b9de4 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -861,7 +861,7 @@
   AddFilter(midi_message_filter_.get());
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled())
+  if (features::IsMashEnabled())
     CreateRenderWidgetWindowTreeClientFactory(GetServiceManagerConnection());
 #endif
 
@@ -1035,7 +1035,7 @@
   categorized_worker_pool_->Start(num_raster_threads);
 
   discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
-  if (features::IsMusEnabled()) {
+  if (features::IsMashEnabled()) {
 #if defined(USE_AURA)
     GetServiceManagerConnection()->GetConnector()->BindInterface(
         ui::mojom::kServiceName, &manager_ptr);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 424f38f..2533785 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2304,6 +2304,12 @@
 }
 
 void RenderViewImpl::SetFocusAndActivateForTesting(bool enable) {
+  // If the main frame is remote, return immediately. Page level focus
+  // should be set from the browser process, so if needed by tests it should
+  // be properly supported.
+  if (webview()->MainFrame()->IsWebRemoteFrame())
+    return;
+
   if (enable) {
     if (has_focus())
       return;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 9d8f93a..f7e9b62 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -425,7 +425,7 @@
   }
 #if defined(USE_AURA)
   RendererWindowTreeClient::CreateIfNecessary(routing_id_);
-  if (features::IsMusEnabled())
+  if (features::IsMashEnabled())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!is_hidden_);
 #endif
 }
@@ -2040,7 +2040,7 @@
   is_hidden_ = hidden;
 
 #if defined(USE_AURA)
-  if (features::IsMusEnabled())
+  if (features::IsMashEnabled())
     RendererWindowTreeClient::Get(routing_id_)->SetVisible(!hidden);
 #endif
 
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index a0560dd..89dd771 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -52,20 +52,6 @@
   ]
 }
 
-shared_library("libcontent_native_test") {
-  testonly = true
-  deps = [
-    ":content_test_jni_registration",
-    "//base",
-    "//content/public/test/android:content_native_test_support",
-    "//content/shell:content_shell_lib",
-  ]
-
-  sources = [
-    "shell_test_library_loader.cc",
-  ]
-}
-
 android_resources("content_shell_java_resources") {
   testonly = true
   resource_dirs = [ "java/res" ]
@@ -211,13 +197,6 @@
   ]
 }
 
-generate_jni_registration("content_test_jni_registration") {
-  testonly = true
-  target = ":content_shell_test_apk__apk"
-  output = "$root_gen_dir/content/shell/android/${target_name}.h"
-  exception_files = jni_exception_files
-}
-
 instrumentation_test_apk("content_shell_test_apk") {
   deps = [
     "//base:base_java_test_support",
@@ -228,7 +207,6 @@
   ]
   apk_under_test = ":content_shell_apk"
   apk_name = "ContentShellTest"
-  shared_libraries = [ ":libcontent_native_test" ]
   android_manifest = "javatests/AndroidManifest.xml"
 }
 
diff --git a/content/shell/android/shell_test_library_loader.cc b/content/shell/android/shell_test_library_loader.cc
deleted file mode 100644
index 7b47e54c..0000000
--- a/content/shell/android/shell_test_library_loader.cc
+++ /dev/null
@@ -1,31 +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 "base/android/jni_android.h"
-#include "base/android/jni_utils.h"
-#include "content/public/app/content_jni_onload.h"
-#include "content/public/app/content_main.h"
-#include "content/public/browser/android/compositor.h"
-#include "content/shell/android/content_test_jni_registration.h"
-#include "content/shell/app/shell_main_delegate.h"
-
-// This is called by the VM when the shared library is first loaded.
-JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
-    if (!RegisterNonMainDexNatives(env)) {
-      return -1;
-    }
-  }
-
-  if (!RegisterMainDexNatives(env)) {
-    return -1;
-  }
-  if (!content::android::OnJNIOnLoadInit())
-    return -1;
-  content::Compositor::Initialize();
-  content::SetContentMainDelegate(new content::ShellMainDelegate());
-  return JNI_VERSION_1_4;
-}
diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc
index 5c284110..ec6bb262 100644
--- a/content/shell/browser/shell_url_request_context_getter.cc
+++ b/content/shell/browser/shell_url_request_context_getter.cc
@@ -49,25 +49,6 @@
 
 namespace content {
 
-namespace {
-
-// TODO(rsleevi): Embedders should see https://crbug.com/700973 before using
-// this pattern.
-class IgnoresCTPolicyEnforcer : public net::CTPolicyEnforcer {
- public:
-  IgnoresCTPolicyEnforcer() = default;
-  ~IgnoresCTPolicyEnforcer() override = default;
-
-  net::ct::CTPolicyCompliance CheckCompliance(
-      net::X509Certificate* cert,
-      const net::SCTList& verified_scts,
-      const net::NetLogWithSource& net_log) override {
-    return net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-};
-
-}  // namespace
-
 ShellURLRequestContextGetter::ShellURLRequestContextGetter(
     bool ignore_certificate_errors,
     bool off_the_record,
@@ -151,7 +132,6 @@
 
     builder.SetCertVerifier(GetCertVerifier());
     builder.set_ct_verifier(std::make_unique<net::DoNothingCTVerifier>());
-    builder.set_ct_policy_enforcer(std::make_unique<IgnoresCTPolicyEnforcer>());
 
     std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service =
         GetProxyService();
diff --git a/content/test/data/site_isolation/js-html-polyglot2.html b/content/test/data/site_isolation/js-html-polyglot2.html
new file mode 100644
index 0000000..14227587
--- /dev/null
+++ b/content/test/data/site_isolation/js-html-polyglot2.html
@@ -0,0 +1,10 @@
+<!-- comment --> <script type='text/javascript'>
+//<![CDATA[
+
+// This is a regression test for https://crbug.com/839945
+// which found out that some script resources are served
+// with text/html content-type and with a body that is
+// both a valid html and a valid javascript.
+var blah = 123;
+
+//]]>--></script>
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index e95e3ac0..f4e82ad 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -44,8 +44,6 @@
     # TODO(vmiura) check / generate reference images for Android devices
     self.Fail('Pixel_SolidColorBackground', ['mac', 'android'], bug=624256)
 
-    self.Fail('Pixel_OffscreenCanvasUnaccelerated2DGPUCompositingWorker',
-        ['mac', ('nvidia', 0xfe9)], bug=706016)
     self.Fail('Pixel_CSSFilterEffects',
         ['mac', ('nvidia', 0xfe9)], bug=690277)
 
@@ -97,9 +95,9 @@
               ['linux', 'mac', 'win'], bug=744658)
 
     # TODO(rjkroege): temporarily suppress this test.
-    self.Flaky('Pixel_OffscreenCanvas2DResizeOnWorker',
-              ['mac', 'nvidia'], bug=840394)
+    self.Flaky('Pixel_OffscreenCanvas2DResizeOnWorker', ['mac'], bug=840394)
 
     # TODO(kbr): temporary suppression for new test.
     self.Flaky('Pixel_WebGLSadCanvas', ['mac'], bug=575305)
+    self.Flaky('Pixel_WebGLSadCanvas', ['win', 'intel'], bug=575305)
     self.Fail('Pixel_WebGLSadCanvas', ['android', 'nvidia'], bug=575305)
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index ba19401..56b36d7 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -240,6 +240,8 @@
     sources += [
       "bluetooth_adapter_winrt.cc",
       "bluetooth_adapter_winrt.h",
+      "bluetooth_device_winrt.cc",
+      "bluetooth_device_winrt.h",
     ]
 
     libs = [
diff --git a/device/bluetooth/bluetooth_device_winrt.cc b/device/bluetooth/bluetooth_device_winrt.cc
new file mode 100644
index 0000000..932f48c
--- /dev/null
+++ b/device/bluetooth/bluetooth_device_winrt.cc
@@ -0,0 +1,168 @@
+// 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 "device/bluetooth/bluetooth_device_winrt.h"
+
+#include "base/logging.h"
+#include "device/bluetooth/bluetooth_adapter_winrt.h"
+
+namespace device {
+
+BluetoothDeviceWinrt::BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter)
+    : BluetoothDevice(adapter) {}
+
+BluetoothDeviceWinrt::~BluetoothDeviceWinrt() = default;
+
+uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+std::string BluetoothDeviceWinrt::GetAddress() const {
+  NOTIMPLEMENTED();
+  return std::string();
+}
+
+BluetoothDevice::VendorIDSource BluetoothDeviceWinrt::GetVendorIDSource()
+    const {
+  NOTIMPLEMENTED();
+  return VendorIDSource();
+}
+
+uint16_t BluetoothDeviceWinrt::GetVendorID() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+uint16_t BluetoothDeviceWinrt::GetProductID() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+uint16_t BluetoothDeviceWinrt::GetDeviceID() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+uint16_t BluetoothDeviceWinrt::GetAppearance() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+base::Optional<std::string> BluetoothDeviceWinrt::GetName() const {
+  NOTIMPLEMENTED();
+  return base::nullopt;
+}
+
+bool BluetoothDeviceWinrt::IsPaired() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::IsConnected() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::IsGattConnected() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::IsConnectable() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::IsConnecting() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::ExpectingPinCode() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::ExpectingPasskey() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool BluetoothDeviceWinrt::ExpectingConfirmation() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void BluetoothDeviceWinrt::GetConnectionInfo(
+    const ConnectionInfoCallback& callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::SetConnectionLatency(
+    ConnectionLatency connection_latency,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::Connect(PairingDelegate* pairing_delegate,
+                                   const base::Closure& callback,
+                                   const ConnectErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::SetPinCode(const std::string& pincode) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::SetPasskey(uint32_t passkey) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::ConfirmPairing() {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::RejectPairing() {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::CancelPairing() {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::Disconnect(const base::Closure& callback,
+                                      const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::Forget(const base::Closure& callback,
+                                  const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::ConnectToService(
+    const BluetoothUUID& uuid,
+    const ConnectToServiceCallback& callback,
+    const ConnectToServiceErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::ConnectToServiceInsecurely(
+    const device::BluetoothUUID& uuid,
+    const ConnectToServiceCallback& callback,
+    const ConnectToServiceErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::CreateGattConnectionImpl() {
+  NOTIMPLEMENTED();
+}
+
+void BluetoothDeviceWinrt::DisconnectGatt() {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace device
diff --git a/device/bluetooth/bluetooth_device_winrt.h b/device/bluetooth/bluetooth_device_winrt.h
new file mode 100644
index 0000000..df656cbd
--- /dev/null
+++ b/device/bluetooth/bluetooth_device_winrt.h
@@ -0,0 +1,77 @@
+// 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 DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_export.h"
+
+namespace device {
+
+class BluetoothAdapterWinrt;
+
+class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
+ public:
+  explicit BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter);
+  ~BluetoothDeviceWinrt() override;
+
+  // BluetoothDevice:
+  uint32_t GetBluetoothClass() const override;
+  std::string GetAddress() const override;
+  VendorIDSource GetVendorIDSource() const override;
+  uint16_t GetVendorID() const override;
+  uint16_t GetProductID() const override;
+  uint16_t GetDeviceID() const override;
+  uint16_t GetAppearance() const override;
+  base::Optional<std::string> GetName() const override;
+  bool IsPaired() const override;
+  bool IsConnected() const override;
+  bool IsGattConnected() const override;
+  bool IsConnectable() const override;
+  bool IsConnecting() const override;
+  bool ExpectingPinCode() const override;
+  bool ExpectingPasskey() const override;
+  bool ExpectingConfirmation() const override;
+  void GetConnectionInfo(const ConnectionInfoCallback& callback) override;
+  void SetConnectionLatency(ConnectionLatency connection_latency,
+                            const base::Closure& callback,
+                            const ErrorCallback& error_callback) override;
+  void Connect(PairingDelegate* pairing_delegate,
+               const base::Closure& callback,
+               const ConnectErrorCallback& error_callback) override;
+  void SetPinCode(const std::string& pincode) override;
+  void SetPasskey(uint32_t passkey) override;
+  void ConfirmPairing() override;
+  void RejectPairing() override;
+  void CancelPairing() override;
+  void Disconnect(const base::Closure& callback,
+                  const ErrorCallback& error_callback) override;
+  void Forget(const base::Closure& callback,
+              const ErrorCallback& error_callback) override;
+  void ConnectToService(
+      const BluetoothUUID& uuid,
+      const ConnectToServiceCallback& callback,
+      const ConnectToServiceErrorCallback& error_callback) override;
+  void ConnectToServiceInsecurely(
+      const device::BluetoothUUID& uuid,
+      const ConnectToServiceCallback& callback,
+      const ConnectToServiceErrorCallback& error_callback) override;
+
+ protected:
+  // BluetoothDevice:
+  void CreateGattConnectionImpl() override;
+  void DisconnectGatt() override;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceWinrt);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc
index 73f31da..056c6c3 100644
--- a/device/usb/usb_device_impl.cc
+++ b/device/usb/usb_device_impl.cc
@@ -41,8 +41,8 @@
                 base::string16(),
                 base::string16(),
                 base::string16()),
-      platform_device_(std::move(platform_device)),
-      context_(std::move(context)) {
+      context_(std::move(context)),
+      platform_device_(std::move(platform_device)) {
   CHECK(platform_device_.is_valid()) << "platform_device must be valid";
   ReadAllConfigurations();
   RefreshActiveConfiguration();
diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h
index d4dce81..16ab891 100644
--- a/device/usb/usb_device_impl.h
+++ b/device/usb/usb_device_impl.h
@@ -84,11 +84,11 @@
               scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
 
   base::ThreadChecker thread_checker_;
-  const ScopedLibusbDeviceRef platform_device_;
   bool visited_ = false;
 
-  // Retain the context so that it will not be released before UsbDevice.
+  // The libusb_context must not be released before the libusb_device.
   const scoped_refptr<UsbContext> context_;
+  const ScopedLibusbDeviceRef platform_device_;
 
   DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl);
 };
diff --git a/device/usb/usb_service_impl.h b/device/usb/usb_service_impl.h
index eaf669d..e7e89d0 100644
--- a/device/usb/usb_service_impl.h
+++ b/device/usb/usb_service_impl.h
@@ -87,6 +87,7 @@
   void EnumerationFailed(ScopedLibusbDeviceRef platform_device,
                          const base::Closure& refresh_complete);
 
+  // The libusb_context must outlive any references to libusb_device objects.
   scoped_refptr<UsbContext> context_;
   bool usb_unavailable_ = false;
 
diff --git a/docs/qtcreator.md b/docs/qtcreator.md
index e9da7d56..9bd13d7 100644
--- a/docs/qtcreator.md
+++ b/docs/qtcreator.md
@@ -3,8 +3,8 @@
 [Qt Creator](https://www.qt.io/ide/)
 ([Wiki](https://en.wikipedia.org/wiki/Qt_Creator)) is a cross-platform C++ IDE.
 
-You can use Qt Creator as a daily IDE or just as a GDB frontend (which does
-not require project configuration).
+You can use Qt Creator as a daily IDE on Linux or Mac, or just as a GDB/LLDB
+frontend (which does not require project configuration).
 
 [TOC]
 
@@ -13,10 +13,10 @@
 ### Workflow features
 
 * Built-in code completion.
-* Navigate to classes, files, or symbols with `ctrl+k`.
+* Navigate to classes, files, or symbols with `ctrl+k` or `cmd+k` (macOS).
 * Switch between declaration and definition with `F2`.
-* Build with `ctrl+shift+b`.
-* Build and run with `ctrl+r`, or debug with `F5`.
+* Build with `ctrl+shift+b` or `shift+cmd+b` (macOS).
+* Build and run with `ctrl+r` or `cmd+r` (macOS), or debug with `F5`.
 * Switch between the header file and cpp file with `F4`.
 
 ### Setup
@@ -26,17 +26,20 @@
 3. Start it with `qtcreator out/Default/qtcreator_project/all.creator`.
 4. Help - Plugins - check ClangCodeModel to enable std completion.
 
-It takes 3 minutes to parse all of chrome's C++ files on my workstation!!! And
+It takes 3 minutes to parse all of chromium's C++ files on my workstation!!! And
 it does not block while parsing.
 
 #### Code Style
 
-1. Help - About Plugins, enable Beautifier.
-2. Tools - Options - Beautifier - Clang Format,
-   change the Clang format command to: `$depot_tools_dir/clang-format`, and
+1. Help - About Plugins (or app menu on macOS), enable Beautifier.
+2. Tools - Options (Preferences on macOS) - Beautifier
+   Make sure to tick - Enable auto format on file save"
+   Select ClangFormat as the tool
+   Go to Clang Format tab
+   Change the Clang format command to: `$chromium_checkout_dir/src/buildtools/$os/clang-format`, and
    set `Use predefined style: file`. You can also set a keyboard shortcut
    for it.
-3. Tools - Options - Code Style, import this xml file.
+3. Tools - Options - C++ - Code Style, import this xml file.
 
 ```
 <?xml version="1.0" encoding="UTF-8"?>
@@ -100,18 +103,24 @@
 ## Debugger
 
 **You can skip the project settings and use QtCreator as a single file
-standalone GDB frontend.**
+standalone GDB or LLDB (macOS) frontend.**
 
+For macOS :
+1. Open the file you want to debug.
+2. Debug - Start Debugging - Attach to running Application, you may need to
+   open chromium's task manager to find the process id.
+
+For Linux :
 1. Tools - Options - Build & Run - Debuggers, make sure GDB is set.
 2. Tools - Options - Kits, change the Desktop kit to GDB (LLDB doesn't work on
    Linux).
 3. Open the file you want to debug.
 4. Debug - Start Debugging - Attach to running Application, you may need to
-   open chrome's task manager to find the process id.
+   open chromium's task manager to find the process id.
 
 ### Tips, tricks, and troubleshooting
 
-#### The debugger exits immediately
+#### [Linux] The debugger exits immediately
 
 Ensure yama allows you to attach to another process:
 
@@ -120,11 +129,15 @@
 ```
 
 
-#### The debugger does not stop on breakpoints
+#### [Linux] The debugger does not stop on breakpoints
 
 Ensure you are using GDB on Linux, not LLDB.
 
 #### More
 
+Linux :
 See
 https://chromium.googlesource.com/chromium/src/+/master/docs/linux_debugging.md
+
+macOS :
+https://dev.chromium.org/developers/how-tos/debugging-on-os-x
diff --git a/docs/testing/layout_tests_tips.md b/docs/testing/layout_tests_tips.md
index 5911cc3..fcda5dd8 100644
--- a/docs/testing/layout_tests_tips.md
+++ b/docs/testing/layout_tests_tips.md
@@ -107,7 +107,7 @@
 
 Tests should provide as much relevant information as possible when failing.
 `testharness.js` tests should prefer
-[rich assert_ functions](https://github.com/w3c/testharness.js/blob/master/docs/api.md#list-of-assertions)
+[rich assert_ functions](https://github.com/w3c/web-platform-tests/blob/master/docs/_writing-tests/testharness-api.md#list-of-assertions)
 to combining `assert_true()` with a boolean operator. Using appropriate
 `assert_` functions results in better diagnostic output when the assertion
 fails.
diff --git a/docs/testing/using_breakpad_with_content_shell.md b/docs/testing/using_breakpad_with_content_shell.md
index c90e26f..534e928 100644
--- a/docs/testing/using_breakpad_with_content_shell.md
+++ b/docs/testing/using_breakpad_with_content_shell.md
@@ -1,7 +1,7 @@
 # Using breakpad with content shell
 
 When running layout tests, it is possible to use
-[breakpad](../../breakpad/) to capture stack traces on crashes while
+[breakpad](../../third_party/breakpad/) to capture stack traces on crashes while
 running without a debugger attached and with the sandbox enabled.
 
 ## Setup
diff --git a/docs/testing/writing_layout_tests.md b/docs/testing/writing_layout_tests.md
index 80b2a53..126707d 100644
--- a/docs/testing/writing_layout_tests.md
+++ b/docs/testing/writing_layout_tests.md
@@ -224,9 +224,9 @@
 
 For example, the most popular Blink-specific API is `testRunner`, which is
 implemented in
-[components/test_runner/test_runner.h](../../components/test_runner/test_runner.h)
+[content/shell/test_runner/test_runner.h](../../content/shell/test_runner/test_runner.h)
 and
-[components/test_runner/test_runner.cc](../../components/test_runner/test_runner.cc).
+[content/shell/test_runner/test_runner.cc](../../content/shell/test_runner/test_runner.cc).
 By skimming the `TestRunnerBindings::Install` method, we learn that the
 testRunner API is presented by the `window.testRunner` and
 `window.layoutTestsController` objects, which are synonyms. Reading the
@@ -248,12 +248,12 @@
 and uses the `testRunner` API.
 ***
 
-See the [components/test_runner/](../../components/test_runner/) directory and
+See the [content/shell/test_runner/](../../content/shell/test_runner/) directory and
 [WebKit's LayoutTests guide](https://trac.webkit.org/wiki/Writing%20Layout%20Tests%20for%20DumpRenderTree)
 for other useful APIs. For example, `window.eventSender`
-([components/test_runner/event_sender.h](../../components/test_runner/event_sender.h)
+([content/shell/test_runner/event_sender.h](../../content/shell/test_runner/event_sender.h)
 and
-[components/test_runner/event_sender.cc](../../components/test_runner/event_sender.cc))
+[content/shell/test_runner/event_sender.cc](../../content/shell/test_runner/event_sender.cc))
 has methods that simulate events input such as keyboard / mouse input and
 drag-and-drop.
 
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index d4bb2a78..1cf7c31 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1310,6 +1310,8 @@
   WALLPAPERPRIVATE_GETCURRENTWALLPAPERTHUMBNAIL,
   ACCESSIBILITY_PRIVATE_ONSELECTTOSPEAKSTATECHANGED,
   INPUTMETHODPRIVATE_GETCOMPOSITIONBOUNDS,
+  FILEMANAGERPRIVATE_ISCROSTINIENABLED,
+  FILEMANAGERPRIVATE_MOUNTCROSTINICONTAINER,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc
index 195d672..40ad83f6 100644
--- a/extensions/browser/updater/update_service_unittest.cc
+++ b/extensions/browser/updater/update_service_unittest.cc
@@ -395,17 +395,11 @@
 
   // Build 3 extensions.
   scoped_refptr<Extension> extension1 =
-      ExtensionBuilder("1")
-          .MergeManifest(DictionaryBuilder().Set("version", "1.2").Build())
-          .Build();
+      ExtensionBuilder("1").SetManifestKey("version", "1.2").Build();
   scoped_refptr<Extension> extension2 =
-      ExtensionBuilder("2")
-          .MergeManifest(DictionaryBuilder().Set("version", "2.3").Build())
-          .Build();
+      ExtensionBuilder("2").SetManifestKey("version", "2.3").Build();
   scoped_refptr<Extension> extension3 =
-      ExtensionBuilder("3")
-          .MergeManifest(DictionaryBuilder().Set("version", "3.4").Build())
-          .Build();
+      ExtensionBuilder("3").SetManifestKey("version", "3.4").Build();
   EXPECT_TRUE(extension1->id() != extension2->id() &&
               extension1->id() != extension3->id() &&
               extension2->id() != extension3->id());
diff --git a/extensions/common/api/_manifest_features.json b/extensions/common/api/_manifest_features.json
index dc2681ed..57920b6 100644
--- a/extensions/common/api/_manifest_features.json
+++ b/extensions/common/api/_manifest_features.json
@@ -75,7 +75,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/407693
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/407693
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/407693
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/407693
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/407693
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   }],
   "content_capabilities": [{
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 72ff9bac..8d81da3 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -146,7 +146,8 @@
       "2F6F6FDB84E0290ABAB7A9D7571EB344821E5F12",  // http://crbug.com/610452
       "5B9E39EA374B136CBE7AED2D872003107642EAD5",  // http://crbug.com/610452
       "E0E94FB0C01FFB9CDC7A5F098C99B5A8D2F95902",  // http://crbug.com/610452
-      "52E0557059A7A28F74ED1D92DDD997E0CCD37806"   // http://crbug.com/610452
+      "52E0557059A7A28F74ED1D92DDD997E0CCD37806",  // http://crbug.com/610452
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "cast": {
@@ -169,7 +170,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/824667#c15
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/824667#c15
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/824667#c15
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/824667#c15
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/824667#c15
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "clipboard": {
@@ -257,7 +259,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/407693
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/407693
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/407693
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/407693
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/407693
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "fileSystem": [{
@@ -355,7 +358,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // http://crbug.com/720495
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // http://crbug.com/720495
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // http://crbug.com/720495
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // http://crbug.com/720495
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // http://crbug.com/720495
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   {
@@ -461,7 +465,8 @@
       "52E0557059A7A28F74ED1D92DDD997E0CCD37806",  // http://crbug.com/610452
       "61FF4757F9420B62B19BA5C96084649339DB31F5",  // http://crbug.com/731941
       "6FB7E1B6C0247B687AC14772E87A117F5F5E4497",  // http://crbug.com/731941
-      "9834387FDA1F66A1B5CA06CB442137B556F12F2A"   // http://crbug.com/772346
+      "9834387FDA1F66A1B5CA06CB442137B556F12F2A",  // http://crbug.com/772346
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   }],
   "networkingPrivate": {
@@ -494,7 +499,8 @@
       "2F6F6FDB84E0290ABAB7A9D7571EB344821E5F12",  // http://crbug.com/610452
       "5B9E39EA374B136CBE7AED2D872003107642EAD5",  // http://crbug.com/610452
       "E0E94FB0C01FFB9CDC7A5F098C99B5A8D2F95902",  // http://crbug.com/610452
-      "52E0557059A7A28F74ED1D92DDD997E0CCD37806"   // http://crbug.com/610452
+      "52E0557059A7A28F74ED1D92DDD997E0CCD37806",  // http://crbug.com/610452
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "power": {
@@ -682,7 +688,8 @@
       "4F25792AF1AA7483936DE29C07806F203C7170A0",  // Stable internal hotrod app
       "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9",  // Beta internal hotrod app
       "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB",  // Alpha internal hotrod app
-      "81986D4F846CEDDDB962643FA501D1780DD441BB"   // Dev internal hotrod app
+      "81986D4F846CEDDDB962643FA501D1780DD441BB",  // Dev internal hotrod app
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   },
   "vpnProvider": {
diff --git a/extensions/common/extension_builder.cc b/extensions/common/extension_builder.cc
index 7e88591..170c4f5 100644
--- a/extensions/common/extension_builder.cc
+++ b/extensions/common/extension_builder.cc
@@ -19,7 +19,7 @@
   std::vector<std::string> permissions;
   base::Optional<ActionType> action;
   base::Optional<BackgroundPage> background_page;
-  std::unique_ptr<base::DictionaryValue> extra;
+  base::Optional<base::Value> extra;
 
   std::unique_ptr<base::DictionaryValue> GetValue() const {
     DictionaryBuilder manifest;
@@ -78,11 +78,20 @@
     }
 
     std::unique_ptr<base::DictionaryValue> result = manifest.Build();
-    if (extra)
-      result->MergeDictionary(extra.get());
+    if (extra) {
+      const base::DictionaryValue* extra_dict = nullptr;
+      extra->GetAsDictionary(&extra_dict);
+      result->MergeDictionary(extra_dict);
+    }
 
     return result;
   }
+
+  base::Value* get_extra() {
+    if (!extra)
+      extra.emplace(base::Value::Type::DICTIONARY);
+    return &extra.value();
+  }
 };
 
 ExtensionBuilder::ExtensionBuilder()
@@ -167,9 +176,9 @@
 ExtensionBuilder& ExtensionBuilder::MergeManifest(
     std::unique_ptr<base::DictionaryValue> manifest) {
   if (manifest_data_) {
-    if (!manifest_data_->extra)
-      manifest_data_->extra = std::make_unique<base::DictionaryValue>();
-    manifest_data_->extra->MergeDictionary(manifest.get());
+    base::DictionaryValue* extra_dict = nullptr;
+    manifest_data_->get_extra()->GetAsDictionary(&extra_dict);
+    extra_dict->MergeDictionary(manifest.get());
   } else {
     manifest_value_->MergeDictionary(manifest.get());
   }
@@ -186,4 +195,17 @@
   return *this;
 }
 
+void ExtensionBuilder::SetManifestKeyImpl(base::StringPiece key,
+                                          base::Value value) {
+  CHECK(manifest_data_);
+  manifest_data_->get_extra()->SetKey(key, std::move(value));
+}
+
+void ExtensionBuilder::SetManifestPathImpl(
+    std::initializer_list<base::StringPiece> path,
+    base::Value value) {
+  CHECK(manifest_data_);
+  manifest_data_->get_extra()->SetPath(path, std::move(value));
+}
+
 }  // namespace extensions
diff --git a/extensions/common/extension_builder.h b/extensions/common/extension_builder.h
index d7c8bd1..df8b887 100644
--- a/extensions/common/extension_builder.h
+++ b/extensions/common/extension_builder.h
@@ -5,12 +5,14 @@
 #ifndef EXTENSIONS_COMMON_EXTENSION_BUILDER_H_
 #define EXTENSIONS_COMMON_EXTENSION_BUILDER_H_
 
+#include <initializer_list>
 #include <memory>
 #include <string>
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/value_builder.h"
 
@@ -87,6 +89,39 @@
   // page will be set.
   ExtensionBuilder& SetBackgroundPage(BackgroundPage background_page);
 
+  // Shortcuts to setting values on the manifest dictionary without needing to
+  // go all the way through MergeManifest(). Sample usage:
+  // ExtensionBuilder("name").SetManifestKey("version", "0.2").Build();
+  // Can be used in conjuction with ListBuilder and DictionaryBuilder for more
+  // complex types.
+  template <typename T>
+  ExtensionBuilder& SetManifestKey(base::StringPiece key, T value) {
+    SetManifestKeyImpl(key, base::Value(value));
+    return *this;
+  }
+  template <typename T>
+  ExtensionBuilder& SetManifestPath(
+      std::initializer_list<base::StringPiece> path,
+      T value) {
+    SetManifestPathImpl(path, base::Value(value));
+    return *this;
+  }
+  // Specializations for unique_ptr<> to allow passing unique_ptr<base::Value>.
+  // All other types will fail to compile.
+  template <typename T>
+  ExtensionBuilder& SetManifestKey(base::StringPiece key,
+                                   std::unique_ptr<T> value) {
+    SetManifestKeyImpl(key, std::move(*value));
+    return *this;
+  }
+  template <typename T>
+  ExtensionBuilder& SetManifestPath(
+      std::initializer_list<base::StringPiece> path,
+      std::unique_ptr<T> value) {
+    SetManifestPathImpl(path, std::move(*value));
+    return *this;
+  }
+
   //////////////////////////////////////////////////////////////////////////////
   // Utility methods for use with custom manifest construction.
 
@@ -120,6 +155,10 @@
  private:
   struct ManifestData;
 
+  void SetManifestKeyImpl(base::StringPiece key, base::Value value);
+  void SetManifestPathImpl(std::initializer_list<base::StringPiece> path,
+                           base::Value value);
+
   // Information for constructing the manifest; either metadata about the
   // manifest which will be used to construct it, or the dictionary itself. Only
   // one will be present.
diff --git a/extensions/common/extension_builder_unittest.cc b/extensions/common/extension_builder_unittest.cc
index 0010b304..79efe1385 100644
--- a/extensions/common/extension_builder_unittest.cc
+++ b/extensions/common/extension_builder_unittest.cc
@@ -167,4 +167,12 @@
   }
 }
 
+TEST(ExtensionBuilderTest, SetManifestKey) {
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder("foo")
+          .SetManifestKey("short_name", "short name")
+          .Build();
+  EXPECT_EQ("short name", extension->short_name());
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/runtime_hooks_delegate_unittest.cc b/extensions/renderer/runtime_hooks_delegate_unittest.cc
index 80013e5b..6a6843a 100644
--- a/extensions/renderer/runtime_hooks_delegate_unittest.cc
+++ b/extensions/renderer/runtime_hooks_delegate_unittest.cc
@@ -104,15 +104,10 @@
   v8::Local<v8::Context> context = MainContext();
 
   {
-    DictionaryBuilder connectable;
-    connectable.Set("matches",
-                    ListBuilder().Append("*://example.com/*").Build());
     scoped_refptr<Extension> connectable_extension =
         ExtensionBuilder("connectable")
-            .MergeManifest(
-                DictionaryBuilder()
-                    .Set("externally_connectable", connectable.Build())
-                    .Build())
+            .SetManifestPath({"externally_connectable", "matches"},
+                             ListBuilder().Append("*://example.com/*").Build())
             .Build();
     RegisterExtension(connectable_extension);
   }
diff --git a/extensions/shell/browser/api/identity/identity_api_unittest.cc b/extensions/shell/browser/api/identity/identity_api_unittest.cc
index 67567dc..f33295773 100644
--- a/extensions/shell/browser/api/identity/identity_api_unittest.cc
+++ b/extensions/shell/browser/api/identity/identity_api_unittest.cc
@@ -68,11 +68,9 @@
                            .Append("https://www.googleapis.com/auth/drive")
                            .Build());
     // Create an extension with OAuth2 scopes.
-    set_extension(
-        ExtensionBuilder("Test")
-            .MergeManifest(
-                DictionaryBuilder().Set("oauth2", oauth2.Build()).Build())
-            .Build());
+    set_extension(ExtensionBuilder("Test")
+                      .SetManifestKey("oauth2", oauth2.Build())
+                      .Build());
   }
 };
 
diff --git a/extensions/shell/browser/system_logs/shell_system_logs_fetcher_unittest.cc b/extensions/shell/browser/system_logs/shell_system_logs_fetcher_unittest.cc
index 991b78f..a0f169a 100644
--- a/extensions/shell/browser/system_logs/shell_system_logs_fetcher_unittest.cc
+++ b/extensions/shell/browser/system_logs/shell_system_logs_fetcher_unittest.cc
@@ -34,7 +34,7 @@
                                           const std::string& version,
                                           const std::string& id) {
     return ExtensionBuilder(name)
-        .MergeManifest(DictionaryBuilder().Set("version", version).Build())
+        .SetManifestKey("version", version)
         .SetID(id)
         .Build();
   }
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc
index 35348f6..7d1e6ad 100644
--- a/google_apis/gcm/tools/mcs_probe.cc
+++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -383,7 +383,7 @@
 
   transport_security_state_ = std::make_unique<net::TransportSecurityState>();
   cert_transparency_verifier_ = std::make_unique<net::MultiLogCTVerifier>();
-  ct_policy_enforcer_ = std::make_unique<net::CTPolicyEnforcer>();
+  ct_policy_enforcer_ = std::make_unique<net::DefaultCTPolicyEnforcer>();
   http_auth_handler_factory_ = net::HttpAuthHandlerRegistryFactory::Create(
       &http_auth_preferences_, host_resolver_.get());
   http_server_properties_ = std::make_unique<net::HttpServerPropertiesImpl>();
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index 8ca97a0..2a688a3 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -462,38 +462,24 @@
 
   os.chdir(base_dir)
 
-  # TODO(backer): Uncomment once the output looks good.
   gen.WriteCommandIds("gpu/command_buffer/common/raster_cmd_ids_autogen.h")
   gen.WriteFormat("gpu/command_buffer/common/raster_cmd_format_autogen.h")
   gen.WriteFormatTest(
     "gpu/command_buffer/common/raster_cmd_format_test_autogen.h")
   gen.WriteGLES2InterfaceHeader(
     "gpu/command_buffer/client/raster_interface_autogen.h")
-  # gen.WriteGLES2InterfaceStub(
-  #   "gpu/command_buffer/client/raster_interface_stub_autogen.h")
-  # gen.WriteGLES2InterfaceStubImpl(
-  #     "gpu/command_buffer/client/raster_interface_stub_impl_autogen.h")
   gen.WriteGLES2ImplementationHeader(
     "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.WriteGLES2TraceImplementationHeader(
-  #     "gpu/command_buffer/client/raster_trace_implementation_autogen.h")
-  # gen.WriteGLES2TraceImplementation(
-  #     "gpu/command_buffer/client/raster_trace_implementation_impl_autogen.h")
-  # gen.WriteGLES2CLibImplementation(
-  #   "gpu/command_buffer/client/raster_c_lib_autogen.h")
   gen.WriteCmdHelperHeader(
      "gpu/command_buffer/client/raster_cmd_helper_autogen.h")
   gen.WriteServiceImplementation(
     "gpu/command_buffer/service/raster_decoder_autogen.h")
   gen.WriteServiceUnitTests(
     "gpu/command_buffer/service/raster_decoder_unittest_%d_autogen.h")
-  # gen.WriteServiceUnitTestsForExtensions(
-  #   "gpu/command_buffer/service/"
-  #   "raster_cmd_decoder_unittest_extensions_autogen.h")
   gen.WriteServiceUtilsHeader(
     "gpu/command_buffer/service/raster_cmd_validation_autogen.h")
   gen.WriteServiceUtilsImplementation(
diff --git a/gpu/command_buffer/client/raster_cmd_helper_autogen.h b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
index 10d9ad3..c252dac6 100644
--- a/gpu/command_buffer/client/raster_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
@@ -134,32 +134,6 @@
   }
 }
 
-void InitializeDiscardableTextureCHROMIUM(GLuint texture_id,
-                                          uint32_t shm_id,
-                                          uint32_t shm_offset) {
-  raster::cmds::InitializeDiscardableTextureCHROMIUM* c =
-      GetCmdSpace<raster::cmds::InitializeDiscardableTextureCHROMIUM>();
-  if (c) {
-    c->Init(texture_id, shm_id, shm_offset);
-  }
-}
-
-void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) {
-  raster::cmds::UnlockDiscardableTextureCHROMIUM* c =
-      GetCmdSpace<raster::cmds::UnlockDiscardableTextureCHROMIUM>();
-  if (c) {
-    c->Init(texture_id);
-  }
-}
-
-void LockDiscardableTextureCHROMIUM(GLuint texture_id) {
-  raster::cmds::LockDiscardableTextureCHROMIUM* c =
-      GetCmdSpace<raster::cmds::LockDiscardableTextureCHROMIUM>();
-  if (c) {
-    c->Init(texture_id);
-  }
-}
-
 void BeginRasterCHROMIUM(GLuint texture_id,
                          GLuint sk_color,
                          GLuint msaa_sample_count,
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index d1b6081..aa59eec 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -377,18 +377,19 @@
 
 bool RasterImplementation::ThreadSafeShallowLockDiscardableTexture(
     uint32_t texture_id) {
-  return discardable_texture_manager_.TextureIsValid(texture_id) &&
-         discardable_texture_manager_.LockTexture(texture_id);
+  NOTREACHED();
+  return false;
 }
 
 void RasterImplementation::CompleteLockDiscardableTexureOnContextThread(
     uint32_t texture_id) {
-  helper_->LockDiscardableTextureCHROMIUM(texture_id);
+  NOTREACHED();
 }
 
 bool RasterImplementation::ThreadsafeDiscardableTextureIsDeletedForTracing(
     uint32_t texture_id) {
-  return discardable_texture_manager_.TextureIsDeletedForTracing(texture_id);
+  NOTREACHED();
+  return false;
 }
 
 const std::string& RasterImplementation::GetLogPrefix() const {
@@ -671,7 +672,6 @@
   helper_->DeleteTexturesImmediate(n, textures);
   for (GLsizei ii = 0; ii < n; ++ii) {
     texture_id_allocator_.FreeID(textures[ii]);
-    discardable_texture_manager_.FreeTexture(textures[ii]);
   }
   UnbindTexturesHelper(n, textures);
 }
@@ -955,57 +955,6 @@
   CheckGLError();
 }
 
-void RasterImplementation::InitializeDiscardableTextureCHROMIUM(
-    GLuint texture_id) {
-  if (discardable_texture_manager_.TextureIsValid(texture_id)) {
-    SetGLError(GL_INVALID_VALUE, "glInitializeDiscardableTextureCHROMIUM",
-               "Texture ID already initialized");
-    return;
-  }
-  ClientDiscardableHandle handle =
-      discardable_texture_manager_.InitializeTexture(helper_->command_buffer(),
-                                                     texture_id);
-  if (!handle.IsValid())
-    return;
-
-  helper_->InitializeDiscardableTextureCHROMIUM(texture_id, handle.shm_id(),
-                                                handle.byte_offset());
-}
-
-void RasterImplementation::UnlockDiscardableTextureCHROMIUM(GLuint texture_id) {
-  if (!discardable_texture_manager_.TextureIsValid(texture_id)) {
-    SetGLError(GL_INVALID_VALUE, "glUnlockDiscardableTextureCHROMIUM",
-               "Texture ID not initialized");
-    return;
-  }
-
-  // |should_unbind_texture| will be set to true if the texture has been fully
-  // unlocked. In this case, ensure the texture is unbound.
-  bool should_unbind_texture = false;
-  discardable_texture_manager_.UnlockTexture(texture_id,
-                                             &should_unbind_texture);
-  if (should_unbind_texture)
-    UnbindTexturesHelper(1, &texture_id);
-
-  helper_->UnlockDiscardableTextureCHROMIUM(texture_id);
-}
-
-bool RasterImplementation::LockDiscardableTextureCHROMIUM(GLuint 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)) {
-    // Failure to lock means that this texture has been deleted on the service
-    // side. Delete it here as well.
-    DeleteTexturesHelper(1, &texture_id);
-    return false;
-  }
-  helper_->LockDiscardableTextureCHROMIUM(texture_id);
-  return true;
-}
-
 void* RasterImplementation::MapRasterCHROMIUM(GLsizeiptr size) {
   if (size < 0) {
     SetGLError(GL_INVALID_VALUE, "glMapRasterCHROMIUM", "negative size");
@@ -1247,10 +1196,10 @@
 }
 
 void RasterImplementation::BeginGpuRaster() {
-  NOTIMPLEMENTED();
+  NOTREACHED();
 }
 void RasterImplementation::EndGpuRaster() {
-  NOTIMPLEMENTED();
+  NOTREACHED();
 }
 
 RasterImplementation::RasterProperties::RasterProperties(
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index e836291a..88adb82 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/trace_event/memory_dump_provider.h"
-#include "gpu/command_buffer/client/client_discardable_texture_manager.h"
 #include "gpu/command_buffer/client/client_font_manager.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gpu_control_client.h"
@@ -278,7 +277,6 @@
   IdAllocator query_id_allocator_;
 
   ClientFontManager font_manager_;
-  ClientDiscardableTextureManager discardable_texture_manager_;
 
   mutable base::Lock lost_lock_;
   bool lost_;
diff --git a/gpu/command_buffer/client/raster_implementation_autogen.h b/gpu/command_buffer/client/raster_implementation_autogen.h
index b2393ae..9b064384 100644
--- a/gpu/command_buffer/client/raster_implementation_autogen.h
+++ b/gpu/command_buffer/client/raster_implementation_autogen.h
@@ -65,12 +65,6 @@
 
 GLenum GetGraphicsResetStatusKHR() override;
 
-void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) override;
-
-void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-
-bool LockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-
 void EndRasterCHROMIUM() override;
 
 GLuint CreateTexture(bool use_buffer,
diff --git a/gpu/command_buffer/client/raster_implementation_gles.cc b/gpu/command_buffer/client/raster_implementation_gles.cc
index 59ed9ab..4ea1276 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles.cc
@@ -2,10 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstddef>
-
 #include "gpu/command_buffer/client/raster_implementation_gles.h"
 
+#include <algorithm>
+#include <cstddef>
+#include <limits>
+#include <set>
+#include <utility>
+#include <vector>
+
 #include "base/logging.h"
 #include "cc/paint/color_space_transfer_cache_entry.h"
 #include "cc/paint/decode_stashing_image_provider.h"
@@ -286,7 +291,7 @@
 
 void RasterImplementationGLES::DeleteTextures(GLsizei n,
                                               const GLuint* textures) {
-  DCHECK(n > 0);
+  DCHECK_GT(n, 0);
   for (GLsizei i = 0; i < n; i++) {
     auto texture_iter = texture_info_.find(textures[i]);
     DCHECK(texture_iter != texture_info_.end());
@@ -298,7 +303,7 @@
   }
 
   gl_->DeleteTextures(n, textures);
-};
+}
 
 void RasterImplementationGLES::SetColorSpaceMetadata(GLuint texture_id,
                                                      GLColorSpace color_space) {
@@ -372,7 +377,7 @@
 
   if (texture->use_buffer) {
     DCHECK(use_texture_storage_image_);
-    DCHECK(levels == 1);
+    DCHECK_EQ(levels, 1);
     gl_->TexStorage2DImageCHROMIUM(texture->target,
                                    viz::TextureStorageFormat(texture->format),
                                    GL_SCANOUT_CHROMIUM, width, height);
@@ -381,7 +386,7 @@
                          viz::TextureStorageFormat(texture->format), width,
                          height);
   } else {
-    DCHECK(levels == 1);
+    DCHECK_EQ(levels, 1);
     // TODO(vmiura): Support more than one texture level.
     gl_->TexImage2D(texture->target, 0, viz::GLInternalFormat(texture->format),
                     width, height, 0, viz::GLDataFormat(texture->format),
@@ -427,24 +432,6 @@
                                           height);
 }
 
-void RasterImplementationGLES::InitializeDiscardableTextureCHROMIUM(
-    GLuint texture_id) {
-  Texture* texture = GetTexture(texture_id);
-  gl_->InitializeDiscardableTextureCHROMIUM(texture->id);
-}
-
-void RasterImplementationGLES::UnlockDiscardableTextureCHROMIUM(
-    GLuint texture_id) {
-  Texture* texture = GetTexture(texture_id);
-  gl_->UnlockDiscardableTextureCHROMIUM(texture->id);
-}
-
-bool RasterImplementationGLES::LockDiscardableTextureCHROMIUM(
-    GLuint texture_id) {
-  Texture* texture = GetTexture(texture_id);
-  return gl_->LockDiscardableTextureCHROMIUM(texture->id);
-}
-
 void RasterImplementationGLES::BeginRasterCHROMIUM(
     GLuint texture_id,
     GLuint sk_color,
@@ -473,7 +460,7 @@
 
   raster_properties_.emplace(sk_color, can_use_lcd_text,
                              raster_color_space.color_space.ToSkColorSpace());
-};
+}
 
 void RasterImplementationGLES::RasterCHROMIUM(
     const cc::DisplayItemList* list,
diff --git a/gpu/command_buffer/client/raster_implementation_gles.h b/gpu/command_buffer/client/raster_implementation_gles.h
index c3fe71c0..7dd2682 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.h
+++ b/gpu/command_buffer/client/raster_implementation_gles.h
@@ -110,11 +110,6 @@
                                           GLsizei width,
                                           GLsizei height) override;
 
-  // Discardable textures.
-  void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) override;
-  void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-  bool LockDiscardableTextureCHROMIUM(GLuint texture_id) override;
-
   // OOP-Raster
   void BeginRasterCHROMIUM(
       GLuint texture_id,
diff --git a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
index d15ff7f6..533bb58 100644
--- a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
@@ -8,6 +8,9 @@
 #include <GLES2/gl2ext.h>
 #include <GLES2/gl2extchromium.h>
 #include <GLES3/gl3.h>
+#include <memory>
+#include <utility>
+#include <vector>
 
 #include "base/containers/flat_map.h"
 #include "cc/paint/color_space_transfer_cache_entry.h"
@@ -135,11 +138,6 @@
                     GLsizei width,
                     GLsizei height));
 
-  // Discardable textures.
-  MOCK_METHOD1(InitializeDiscardableTextureCHROMIUM, void(GLuint texture_id));
-  MOCK_METHOD1(UnlockDiscardableTextureCHROMIUM, void(GLuint texture_id));
-  MOCK_METHOD1(LockDiscardableTextureCHROMIUM, bool(GLuint texture_id));
-
   // OOP-Raster
   MOCK_METHOD6(BeginRasterCHROMIUM,
                void(GLuint texture_id,
@@ -638,41 +636,6 @@
   }
 }
 
-TEST_F(RasterImplementationGLESTest, InitializeDiscardableTextureCHROMIUM) {
-  const GLuint kTextureId = 23;
-
-  AllocTextureId(false, gfx::BufferUsage::GPU_READ, viz::RGBA_8888, kTextureId);
-
-  EXPECT_CALL(*gl_, InitializeDiscardableTextureCHROMIUM(kTextureId)).Times(1);
-  ri_->InitializeDiscardableTextureCHROMIUM(kTextureId);
-}
-
-TEST_F(RasterImplementationGLESTest, UnlockDiscardableTextureCHROMIUM) {
-  const GLuint kTextureId = 23;
-
-  AllocTextureId(false, gfx::BufferUsage::GPU_READ, viz::RGBA_8888, kTextureId);
-
-  EXPECT_CALL(*gl_, UnlockDiscardableTextureCHROMIUM(kTextureId)).Times(1);
-  ri_->UnlockDiscardableTextureCHROMIUM(kTextureId);
-}
-
-TEST_F(RasterImplementationGLESTest, LockDiscardableTextureCHROMIUM) {
-  const GLuint kTextureId = 23;
-  bool ret = false;
-
-  AllocTextureId(false, gfx::BufferUsage::GPU_READ, viz::RGBA_8888, kTextureId);
-
-  EXPECT_CALL(*gl_, LockDiscardableTextureCHROMIUM(kTextureId))
-      .WillOnce(Return(true));
-  ret = ri_->LockDiscardableTextureCHROMIUM(kTextureId);
-  EXPECT_EQ(true, ret);
-
-  EXPECT_CALL(*gl_, LockDiscardableTextureCHROMIUM(kTextureId))
-      .WillOnce(Return(false));
-  ret = ri_->LockDiscardableTextureCHROMIUM(kTextureId);
-  EXPECT_EQ(false, ret);
-}
-
 TEST_F(RasterImplementationGLESTest, RasterCHROMIUM) {
   const GLuint texture_id = 23;
   const GLuint sk_color = 0x226688AAu;
diff --git a/gpu/command_buffer/client/raster_implementation_unittest.cc b/gpu/command_buffer/client/raster_implementation_unittest.cc
index 5e1fa681..76df1d97 100644
--- a/gpu/command_buffer/client/raster_implementation_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_unittest.cc
@@ -200,10 +200,6 @@
 
   QueryTracker* GetQueryTracker() { return gl_->query_tracker_.get(); }
 
-  ClientDiscardableTextureManager* discardable_texture_manager() {
-    return &gl_->discardable_texture_manager_;
-  }
-
   void* MapRasterCHROMIUM(GLsizeiptr size) {
     return gl_->MapRasterCHROMIUM(size);
   }
@@ -905,85 +901,6 @@
   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());
-}
-
-/*
-TODO(vmiura): Update use of BindTexture.
-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"
 
diff --git a/gpu/command_buffer/client/raster_interface_autogen.h b/gpu/command_buffer/client/raster_interface_autogen.h
index e88018d..abdfc4e 100644
--- a/gpu/command_buffer/client/raster_interface_autogen.h
+++ b/gpu/command_buffer/client/raster_interface_autogen.h
@@ -44,9 +44,6 @@
                                                 GLsizei width,
                                                 GLsizei height) = 0;
 virtual GLenum GetGraphicsResetStatusKHR() = 0;
-virtual void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
-virtual void UnlockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
-virtual bool LockDiscardableTextureCHROMIUM(GLuint texture_id) = 0;
 virtual void EndRasterCHROMIUM() = 0;
 virtual GLuint CreateTexture(bool use_buffer,
                              gfx::BufferUsage buffer_usage,
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index a65a890..faad7ed 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -622,122 +622,6 @@
     offsetof(UnpremultiplyAndDitherCopyCHROMIUM, height) == 24,
     "offset of UnpremultiplyAndDitherCopyCHROMIUM height should be 24");
 
-struct InitializeDiscardableTextureCHROMIUM {
-  typedef InitializeDiscardableTextureCHROMIUM ValueType;
-  static const CommandId kCmdId = kInitializeDiscardableTextureCHROMIUM;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLuint _texture_id, uint32_t _shm_id, uint32_t _shm_offset) {
-    SetHeader();
-    texture_id = _texture_id;
-    shm_id = _shm_id;
-    shm_offset = _shm_offset;
-  }
-
-  void* Set(void* cmd,
-            GLuint _texture_id,
-            uint32_t _shm_id,
-            uint32_t _shm_offset) {
-    static_cast<ValueType*>(cmd)->Init(_texture_id, _shm_id, _shm_offset);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t texture_id;
-  uint32_t shm_id;
-  uint32_t shm_offset;
-};
-
-static_assert(sizeof(InitializeDiscardableTextureCHROMIUM) == 16,
-              "size of InitializeDiscardableTextureCHROMIUM should be 16");
-static_assert(
-    offsetof(InitializeDiscardableTextureCHROMIUM, header) == 0,
-    "offset of InitializeDiscardableTextureCHROMIUM header should be 0");
-static_assert(
-    offsetof(InitializeDiscardableTextureCHROMIUM, texture_id) == 4,
-    "offset of InitializeDiscardableTextureCHROMIUM texture_id should be 4");
-static_assert(
-    offsetof(InitializeDiscardableTextureCHROMIUM, shm_id) == 8,
-    "offset of InitializeDiscardableTextureCHROMIUM shm_id should be 8");
-static_assert(
-    offsetof(InitializeDiscardableTextureCHROMIUM, shm_offset) == 12,
-    "offset of InitializeDiscardableTextureCHROMIUM shm_offset should be 12");
-
-struct UnlockDiscardableTextureCHROMIUM {
-  typedef UnlockDiscardableTextureCHROMIUM ValueType;
-  static const CommandId kCmdId = kUnlockDiscardableTextureCHROMIUM;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLuint _texture_id) {
-    SetHeader();
-    texture_id = _texture_id;
-  }
-
-  void* Set(void* cmd, GLuint _texture_id) {
-    static_cast<ValueType*>(cmd)->Init(_texture_id);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t texture_id;
-};
-
-static_assert(sizeof(UnlockDiscardableTextureCHROMIUM) == 8,
-              "size of UnlockDiscardableTextureCHROMIUM should be 8");
-static_assert(offsetof(UnlockDiscardableTextureCHROMIUM, header) == 0,
-              "offset of UnlockDiscardableTextureCHROMIUM header should be 0");
-static_assert(
-    offsetof(UnlockDiscardableTextureCHROMIUM, texture_id) == 4,
-    "offset of UnlockDiscardableTextureCHROMIUM texture_id should be 4");
-
-struct LockDiscardableTextureCHROMIUM {
-  typedef LockDiscardableTextureCHROMIUM ValueType;
-  static const CommandId kCmdId = kLockDiscardableTextureCHROMIUM;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLuint _texture_id) {
-    SetHeader();
-    texture_id = _texture_id;
-  }
-
-  void* Set(void* cmd, GLuint _texture_id) {
-    static_cast<ValueType*>(cmd)->Init(_texture_id);
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t texture_id;
-};
-
-static_assert(sizeof(LockDiscardableTextureCHROMIUM) == 8,
-              "size of LockDiscardableTextureCHROMIUM should be 8");
-static_assert(offsetof(LockDiscardableTextureCHROMIUM, header) == 0,
-              "offset of LockDiscardableTextureCHROMIUM header should be 0");
-static_assert(
-    offsetof(LockDiscardableTextureCHROMIUM, texture_id) == 4,
-    "offset of LockDiscardableTextureCHROMIUM texture_id should be 4");
-
 struct BeginRasterCHROMIUM {
   typedef BeginRasterCHROMIUM ValueType;
   static const CommandId kCmdId = kBeginRasterCHROMIUM;
diff --git a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
index 59cbf6a..84bd76f 100644
--- a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
@@ -207,45 +207,6 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(RasterFormatTest, InitializeDiscardableTextureCHROMIUM) {
-  cmds::InitializeDiscardableTextureCHROMIUM& cmd =
-      *GetBufferAs<cmds::InitializeDiscardableTextureCHROMIUM>();
-  void* next_cmd =
-      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<uint32_t>(12),
-              static_cast<uint32_t>(13));
-  EXPECT_EQ(
-      static_cast<uint32_t>(cmds::InitializeDiscardableTextureCHROMIUM::kCmdId),
-      cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
-  EXPECT_EQ(static_cast<uint32_t>(12), cmd.shm_id);
-  EXPECT_EQ(static_cast<uint32_t>(13), cmd.shm_offset);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
-TEST_F(RasterFormatTest, UnlockDiscardableTextureCHROMIUM) {
-  cmds::UnlockDiscardableTextureCHROMIUM& cmd =
-      *GetBufferAs<cmds::UnlockDiscardableTextureCHROMIUM>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11));
-  EXPECT_EQ(
-      static_cast<uint32_t>(cmds::UnlockDiscardableTextureCHROMIUM::kCmdId),
-      cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
-TEST_F(RasterFormatTest, LockDiscardableTextureCHROMIUM) {
-  cmds::LockDiscardableTextureCHROMIUM& cmd =
-      *GetBufferAs<cmds::LockDiscardableTextureCHROMIUM>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11));
-  EXPECT_EQ(static_cast<uint32_t>(cmds::LockDiscardableTextureCHROMIUM::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
 TEST_F(RasterFormatTest, BeginRasterCHROMIUM) {
   cmds::BeginRasterCHROMIUM& cmd = *GetBufferAs<cmds::BeginRasterCHROMIUM>();
   void* next_cmd =
diff --git a/gpu/command_buffer/common/raster_cmd_ids_autogen.h b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
index ea5a391..e64146a2 100644
--- a/gpu/command_buffer/common/raster_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
@@ -26,24 +26,21 @@
   OP(InsertFenceSyncCHROMIUM)                  /* 267 */ \
   OP(WaitSyncTokenCHROMIUM)                    /* 268 */ \
   OP(UnpremultiplyAndDitherCopyCHROMIUM)       /* 269 */ \
-  OP(InitializeDiscardableTextureCHROMIUM)     /* 270 */ \
-  OP(UnlockDiscardableTextureCHROMIUM)         /* 271 */ \
-  OP(LockDiscardableTextureCHROMIUM)           /* 272 */ \
-  OP(BeginRasterCHROMIUM)                      /* 273 */ \
-  OP(RasterCHROMIUM)                           /* 274 */ \
-  OP(EndRasterCHROMIUM)                        /* 275 */ \
-  OP(CreateTransferCacheEntryINTERNAL)         /* 276 */ \
-  OP(DeleteTransferCacheEntryINTERNAL)         /* 277 */ \
-  OP(UnlockTransferCacheEntryINTERNAL)         /* 278 */ \
-  OP(CreateTexture)                            /* 279 */ \
-  OP(SetColorSpaceMetadata)                    /* 280 */ \
-  OP(ProduceTextureDirectImmediate)            /* 281 */ \
-  OP(CreateAndConsumeTextureINTERNALImmediate) /* 282 */ \
-  OP(TexParameteri)                            /* 283 */ \
-  OP(BindTexImage2DCHROMIUM)                   /* 284 */ \
-  OP(ReleaseTexImage2DCHROMIUM)                /* 285 */ \
-  OP(TexStorage2D)                             /* 286 */ \
-  OP(CopySubTexture)                           /* 287 */
+  OP(BeginRasterCHROMIUM)                      /* 270 */ \
+  OP(RasterCHROMIUM)                           /* 271 */ \
+  OP(EndRasterCHROMIUM)                        /* 272 */ \
+  OP(CreateTransferCacheEntryINTERNAL)         /* 273 */ \
+  OP(DeleteTransferCacheEntryINTERNAL)         /* 274 */ \
+  OP(UnlockTransferCacheEntryINTERNAL)         /* 275 */ \
+  OP(CreateTexture)                            /* 276 */ \
+  OP(SetColorSpaceMetadata)                    /* 277 */ \
+  OP(ProduceTextureDirectImmediate)            /* 278 */ \
+  OP(CreateAndConsumeTextureINTERNALImmediate) /* 279 */ \
+  OP(TexParameteri)                            /* 280 */ \
+  OP(BindTexImage2DCHROMIUM)                   /* 281 */ \
+  OP(ReleaseTexImage2DCHROMIUM)                /* 282 */ \
+  OP(TexStorage2D)                             /* 283 */ \
+  OP(CopySubTexture)                           /* 284 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/raster_cmd_buffer_functions.txt b/gpu/command_buffer/raster_cmd_buffer_functions.txt
index 05437553..f616108 100644
--- a/gpu/command_buffer/raster_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/raster_cmd_buffer_functions.txt
@@ -32,11 +32,6 @@
 // Extension KHR_robustness
 GL_APICALL GLenum	GL_APIENTRY glGetGraphicsResetStatusKHR (void);
 
-// Extension CHROMIUM_discardable_textures
-GL_APICALL void         GL_APIENTRY glInitializeDiscardableTextureCHROMIUM (GLuint texture_id);
-GL_APICALL void         GL_APIENTRY glUnlockDiscardableTextureCHROMIUM (GLuint texture_id);
-GL_APICALL bool         GL_APIENTRY glLockDiscardableTextureCHROMIUM (GLuint texture_id);
-
 // Extension CHROMIUM_raster_transport
 GL_APICALL void         GL_APIENTRY glBeginRasterCHROMIUM (GLuint texture_id, GLuint sk_color, GLuint msaa_sample_count, GLboolean can_use_lcd_text, GLint color_type, GLuint color_space_transfer_cache_id);
 GL_APICALL void         GL_APIENTRY glRasterCHROMIUM (GLuint raster_shm_id, GLuint raster_shm_offset, GLsizeiptr raster_shm_size, GLuint font_shm_id, GLuint font_shm_offset, GLsizeiptr font_shm_size);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 8097ccf3..a10c7d2 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -20183,6 +20183,12 @@
   }
   ~TransferCacheDeserializeHelperImpl() override = default;
 
+  void CreateLocalEntry(
+      uint32_t id,
+      std::unique_ptr<cc::ServiceTransferCacheEntry> entry) override {
+    transfer_cache_->CreateLocalEntry(id, std::move(entry));
+  }
+
  private:
   cc::ServiceTransferCacheEntry* GetEntryInternal(
       cc::TransferCacheEntryType entry_type,
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 0dab7fc..25e2fd6 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -699,6 +699,7 @@
   sk_sp<GrContext> gr_context_;
   sk_sp<SkSurface> sk_surface_;
   std::unique_ptr<SkCanvas> raster_canvas_;
+  uint32_t raster_color_space_id_;
 
   base::WeakPtrFactory<DecoderContext> weak_ptr_factory_;
 
@@ -1766,27 +1767,6 @@
   return error::kNoError;
 }
 
-error::Error RasterDecoderImpl::HandleInitializeDiscardableTextureCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  NOTIMPLEMENTED();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleUnlockDiscardableTextureCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  NOTIMPLEMENTED();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleLockDiscardableTextureCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  NOTIMPLEMENTED();
-  return error::kNoError;
-}
-
 error::Error RasterDecoderImpl::HandleInsertFenceSyncCHROMIUM(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
@@ -2691,6 +2671,12 @@
   }
   ~TransferCacheDeserializeHelperImpl() override = default;
 
+  void CreateLocalEntry(
+      uint32_t id,
+      std::unique_ptr<cc::ServiceTransferCacheEntry> entry) override {
+    transfer_cache_->CreateLocalEntry(id, std::move(entry));
+  }
+
  private:
   cc::ServiceTransferCacheEntry* GetEntryInternal(
       cc::TransferCacheEntryType entry_type,
@@ -2841,6 +2827,7 @@
   raster_canvas_ = SkCreateColorSpaceXformCanvas(
       sk_surface_->getCanvas(),
       color_space_entry->color_space().ToSkColorSpace());
+  raster_color_space_id_ = color_space_transfer_cache_id;
 
   // All or nothing clearing, as no way to validate the client's input on what
   // is the "used" part of the texture.
@@ -2910,6 +2897,7 @@
   TransferCacheDeserializeHelperImpl impl(transfer_cache_.get());
   options.transfer_cache = &impl;
   options.strike_client = font_manager_.strike_client();
+  options.raster_color_space_id = raster_color_space_id_;
 
   int op_idx = 0;
   size_t paint_buffer_size = raster_shm_size;
diff --git a/gpu/command_buffer/service/service_transfer_cache.cc b/gpu/command_buffer/service/service_transfer_cache.cc
index 4a20a60..faea324d 100644
--- a/gpu/command_buffer/service/service_transfer_cache.cc
+++ b/gpu/command_buffer/service/service_transfer_cache.cc
@@ -19,7 +19,7 @@
 }  // namespace
 
 ServiceTransferCache::CacheEntryInternal::CacheEntryInternal(
-    ServiceDiscardableHandle handle,
+    base::Optional<ServiceDiscardableHandle> handle,
     std::unique_ptr<cc::ServiceTransferCacheEntry> entry)
     : handle(handle), entry(std::move(entry)) {}
 
@@ -63,6 +63,21 @@
   return true;
 }
 
+void ServiceTransferCache::CreateLocalEntry(
+    uint32_t entry_id,
+    std::unique_ptr<cc::ServiceTransferCacheEntry> entry) {
+  if (!entry)
+    return;
+
+  DeleteEntry(entry->Type(), entry_id);
+
+  total_size_ += entry->CachedSize();
+
+  auto key = std::make_pair(entry->Type(), entry_id);
+  entries_.Put(key, CacheEntryInternal(base::nullopt, std::move(entry)));
+  EnforceLimits();
+}
+
 bool ServiceTransferCache::UnlockEntry(cc::TransferCacheEntryType entry_type,
                                        uint32_t entry_id) {
   auto key = std::make_pair(entry_type, entry_id);
@@ -70,7 +85,9 @@
   if (found == entries_.end())
     return false;
 
-  found->second.handle.Unlock();
+  if (!found->second.handle)
+    return false;
+  found->second.handle->Unlock();
   return true;
 }
 
@@ -81,7 +98,8 @@
   if (found == entries_.end())
     return false;
 
-  found->second.handle.ForceDelete();
+  if (found->second.handle)
+    found->second.handle->ForceDelete();
   total_size_ -= found->second.entry->CachedSize();
   entries_.Erase(found);
   return true;
@@ -102,7 +120,7 @@
     if (total_size_ <= cache_size_limit_) {
       return;
     }
-    if (!it->second.handle.Delete()) {
+    if (it->second.handle && !it->second.handle->Delete()) {
       ++it;
       continue;
     }
diff --git a/gpu/command_buffer/service/service_transfer_cache.h b/gpu/command_buffer/service/service_transfer_cache.h
index 4cacac1..883d0eb 100644
--- a/gpu/command_buffer/service/service_transfer_cache.h
+++ b/gpu/command_buffer/service/service_transfer_cache.h
@@ -34,6 +34,8 @@
                          ServiceDiscardableHandle handle,
                          GrContext* context,
                          base::span<uint8_t> data);
+  void CreateLocalEntry(uint32_t entry_id,
+                        std::unique_ptr<cc::ServiceTransferCacheEntry> entry);
   bool UnlockEntry(cc::TransferCacheEntryType entry_type, uint32_t entry_id);
   bool DeleteEntry(cc::TransferCacheEntryType entry_type, uint32_t entry_id);
   cc::ServiceTransferCacheEntry* GetEntry(cc::TransferCacheEntryType entry_type,
@@ -49,12 +51,12 @@
   void EnforceLimits();
 
   struct CacheEntryInternal {
-    CacheEntryInternal(ServiceDiscardableHandle handle,
+    CacheEntryInternal(base::Optional<ServiceDiscardableHandle> handle,
                        std::unique_ptr<cc::ServiceTransferCacheEntry> entry);
     CacheEntryInternal(CacheEntryInternal&& other);
     CacheEntryInternal& operator=(CacheEntryInternal&& other);
     ~CacheEntryInternal();
-    ServiceDiscardableHandle handle;
+    base::Optional<ServiceDiscardableHandle> handle;
     std::unique_ptr<cc::ServiceTransferCacheEntry> entry;
   };
   using EntryCache =
diff --git a/gpu/config/gpu_blacklist_unittest.cc b/gpu/config/gpu_blacklist_unittest.cc
index 17f2bf80c..e3c14679 100644
--- a/gpu/config/gpu_blacklist_unittest.cc
+++ b/gpu/config/gpu_blacklist_unittest.cc
@@ -46,6 +46,8 @@
             nullptr,                               // driver info
             nullptr,                               // GL strings
             nullptr,                               // machine model info
+            0,                                     // gpu_series size
+            nullptr,                               // gpu_series
             nullptr,                               // more conditions
         },
         0,        // exceptions count
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index a34cc7fc..720d8794 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -320,7 +320,7 @@
     if (os_version.IsSpecified() && !os_version.Contains(target_os_version))
       return false;
   }
-  if (vendor_id != 0) {
+  if (vendor_id != 0 || gpu_series_list_size > 0) {
     std::vector<GPUInfo::GPUDevice> candidates;
     switch (multi_gpu_category) {
       case kMultiGpuCategoryPrimary:
@@ -346,26 +346,41 @@
           candidates.push_back(gpu_info.gpu);
     }
 
-    GPUInfo::GPUDevice gpu;
-    gpu.vendor_id = vendor_id;
     bool found = false;
-    if (device_id_size == 0) {
-      for (size_t ii = 0; ii < candidates.size(); ++ii) {
-        if (gpu.vendor_id == candidates[ii].vendor_id) {
-          found = true;
-          break;
+    if (gpu_series_list_size > 0) {
+      for (size_t ii = 0; !found && ii < candidates.size(); ++ii) {
+        GpuSeriesType candidate_series = GetGpuSeriesType(
+            candidates[ii].vendor_id, candidates[ii].device_id);
+        if (candidate_series == GpuSeriesType::kUnknown)
+          continue;
+        for (size_t jj = 0; jj < gpu_series_list_size; ++jj) {
+          if (candidate_series == gpu_series_list[jj]) {
+            found = true;
+            break;
+          }
         }
       }
     } else {
-      for (size_t ii = 0; ii < device_id_size; ++ii) {
-        gpu.device_id = device_ids[ii];
-        for (size_t jj = 0; jj < candidates.size(); ++jj) {
-          if (gpu.vendor_id == candidates[jj].vendor_id &&
-              gpu.device_id == candidates[jj].device_id) {
+      GPUInfo::GPUDevice gpu;
+      gpu.vendor_id = vendor_id;
+      if (device_id_size == 0) {
+        for (size_t ii = 0; ii < candidates.size(); ++ii) {
+          if (gpu.vendor_id == candidates[ii].vendor_id) {
             found = true;
             break;
           }
         }
+      } else {
+        for (size_t ii = 0; ii < device_id_size; ++ii) {
+          gpu.device_id = device_ids[ii];
+          for (size_t jj = 0; jj < candidates.size(); ++jj) {
+            if (gpu.vendor_id == candidates[jj].vendor_id &&
+                gpu.device_id == candidates[jj].device_id) {
+              found = true;
+              break;
+            }
+          }
+        }
       }
     }
     if (!found)
@@ -687,4 +702,58 @@
   return true;
 }
 
+// static
+GpuControlList::GpuSeriesType GpuControlList::GetGpuSeriesType(
+    uint32_t vendor_id,
+    uint32_t device_id) {
+  if (vendor_id == 0x8086) {  // Intel
+    // https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units
+    // We only identify Intel 6th gen or newer.
+    uint32_t masked_device_id = device_id & 0xFF00;
+    switch (masked_device_id) {
+      case 0x0100:
+        switch (device_id & 0xFFF0) {
+          case 0x0100:
+          case 0x0110:
+          case 0x0120:
+            return GpuSeriesType::kIntelSandyBridge;
+          case 0x0150:
+            if (device_id == 0x0155 || device_id == 0x0157)
+              return GpuSeriesType::kIntelValleyView;
+            if (device_id == 0x0152 || device_id == 0x015A)
+              return GpuSeriesType::kIntelIvyBridge;
+            break;
+          case 0x0160:
+            return GpuSeriesType::kIntelIvyBridge;
+          default:
+            break;
+        }
+        break;
+      case 0x0F00:
+        return GpuSeriesType::kIntelValleyView;
+      case 0x0400:
+      case 0x0A00:
+      case 0x0D00:
+        return GpuSeriesType::kIntelHaswell;
+      case 0x2200:
+        return GpuSeriesType::kIntelCherryView;
+      case 0x1600:
+        return GpuSeriesType::kIntelBroadwell;
+      case 0x5A00:
+        return GpuSeriesType::kIntelApolloLake;
+      case 0x1900:
+        return GpuSeriesType::kIntelSkyLake;
+      case 0x3100:
+        return GpuSeriesType::kIntelGeminiLake;
+      case 0x5900:
+        return GpuSeriesType::kIntelKabyLake;
+      case 0x3E00:
+        return GpuSeriesType::kIntelCoffeeLake;
+      default:
+        break;
+    }
+  }
+  return GpuSeriesType::kUnknown;
+}
+
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h
index 416386d7..608b51b 100644
--- a/gpu/config/gpu_control_list.h
+++ b/gpu/config/gpu_control_list.h
@@ -84,6 +84,27 @@
     kVersionStyleUnknown
   };
 
+  enum class GpuSeriesType {
+    // Intel 6th gen
+    kIntelSandyBridge,
+    // Intel 7th gen
+    kIntelValleyView,  // BayTrail
+    kIntelIvyBridge,
+    kIntelHaswell,
+    // Intel 8th gen
+    kIntelCherryView,  // Braswell
+    kIntelBroadwell,
+    // Intel 9th gen
+    kIntelApolloLake,
+    kIntelSkyLake,
+    kIntelGeminiLake,
+    kIntelKabyLake,
+    kIntelCoffeeLake,
+    // Please also update |gpu_series_map| in process_json.py.
+
+    kUnknown,
+  };
+
   struct GPU_EXPORT Version {
     NumericOp op;
     VersionStyle style;
@@ -173,6 +194,8 @@
     const DriverInfo* driver_info;
     const GLStrings* gl_strings;
     const MachineModelInfo* machine_model_info;
+    size_t gpu_series_list_size;
+    const GpuSeriesType* gpu_series_list;
     const More* more;
 
     bool Contains(OsType os_type,
@@ -292,6 +315,8 @@
   // Gets the current OS type.
   static OsType GetOsType();
 
+  static GpuSeriesType GetGpuSeriesType(uint32_t vendor_id, uint32_t device_id);
+
   size_t entry_count_;
   const Entry* entries_;
   // This records all the entries that are appliable to the current user
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc
index a1c9971..312adfbd4 100644
--- a/gpu/config/gpu_control_list_entry_unittest.cc
+++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -665,4 +665,185 @@
   EXPECT_TRUE(entry.Contains(kOsLinux, "7.0", gpu_info));
 }
 
+TEST_F(GpuControlListEntryTest, GpuSeries) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeries);
+  GPUInfo gpu_info;
+  gpu_info.gpu.vendor_id = 0x8086;
+  // Intel KabyLake
+  gpu_info.gpu.device_id = 0x5916;
+  EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  // Intel SandyBridge
+  gpu_info.gpu.device_id = 0x0116;
+  EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  // Intel SkyLake
+  gpu_info.gpu.device_id = 0x1916;
+  EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  // Non-Intel GPU
+  gpu_info.gpu.vendor_id = 0x10de;
+  gpu_info.gpu.device_id = 0x0df8;
+  EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+}
+
+TEST_F(GpuControlListEntryTest, GpuSeriesActive) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeriesActive);
+
+  GPUInfo::GPUDevice intel_gpu;
+  intel_gpu.vendor_id = 0x8086;
+  intel_gpu.device_id = 0x5916;
+  GPUInfo::GPUDevice nvidia_gpu;
+  nvidia_gpu.vendor_id = 0x10de;
+  nvidia_gpu.device_id = 0x0df8;
+
+  {  // Single GPU
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is primary and active
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    gpu_info.gpu.active = true;
+    gpu_info.secondary_gpus.push_back(nvidia_gpu);
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is secondary and active
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    gpu_info.secondary_gpus.push_back(intel_gpu);
+    gpu_info.secondary_gpus[0].active = true;
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, NVidia is primary and active
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    gpu_info.gpu.active = true;
+    gpu_info.secondary_gpus.push_back(intel_gpu);
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, NVidia is secondary and active
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    gpu_info.secondary_gpus.push_back(nvidia_gpu);
+    gpu_info.secondary_gpus[0].active = true;
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+}
+
+TEST_F(GpuControlListEntryTest, GpuSeriesAny) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeriesAny);
+
+  GPUInfo::GPUDevice intel_gpu;
+  intel_gpu.vendor_id = 0x8086;
+  intel_gpu.device_id = 0x5916;
+  GPUInfo::GPUDevice nvidia_gpu;
+  nvidia_gpu.vendor_id = 0x10de;
+  nvidia_gpu.device_id = 0x0df8;
+
+  {  // Single GPU Intel
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Single GPU NVidia
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is primary
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    gpu_info.secondary_gpus.push_back(nvidia_gpu);
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is secondary
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    gpu_info.secondary_gpus.push_back(intel_gpu);
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+}
+
+TEST_F(GpuControlListEntryTest, GpuSeriesPrimary) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeriesPrimary);
+
+  GPUInfo::GPUDevice intel_gpu;
+  intel_gpu.vendor_id = 0x8086;
+  intel_gpu.device_id = 0x5916;
+  GPUInfo::GPUDevice nvidia_gpu;
+  nvidia_gpu.vendor_id = 0x10de;
+  nvidia_gpu.device_id = 0x0df8;
+
+  {  // Single GPU
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is primary
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    gpu_info.secondary_gpus.push_back(nvidia_gpu);
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is secondary
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    gpu_info.secondary_gpus.push_back(intel_gpu);
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+}
+
+TEST_F(GpuControlListEntryTest, GpuSeriesSecondary) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeriesSecondary);
+
+  GPUInfo::GPUDevice intel_gpu;
+  intel_gpu.vendor_id = 0x8086;
+  intel_gpu.device_id = 0x5916;
+  GPUInfo::GPUDevice nvidia_gpu;
+  nvidia_gpu.vendor_id = 0x10de;
+  nvidia_gpu.device_id = 0x0df8;
+
+  {  // Single GPU
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is primary
+    GPUInfo gpu_info;
+    gpu_info.gpu = intel_gpu;
+    gpu_info.secondary_gpus.push_back(nvidia_gpu);
+    EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+
+  {  // Dual GPU, Intel is secondary
+    GPUInfo gpu_info;
+    gpu_info.gpu = nvidia_gpu;
+    gpu_info.secondary_gpus.push_back(intel_gpu);
+    EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+  }
+}
+
+TEST_F(GpuControlListEntryTest, GpuSeriesInException) {
+  const Entry& entry = GetEntry(kGpuControlListEntryTest_GpuSeriesInException);
+
+  GPUInfo gpu_info;
+  // Intel KabyLake
+  gpu_info.gpu.vendor_id = 0x8086;
+  gpu_info.gpu.device_id = 0x5916;
+  EXPECT_FALSE(entry.Contains(kOsWin, "10.0", gpu_info));
+  // Intel SandyBridge
+  gpu_info.gpu.vendor_id = 0x8086;
+  gpu_info.gpu.device_id = 0x0116;
+  EXPECT_TRUE(entry.Contains(kOsWin, "10.0", gpu_info));
+}
+
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt
index 449263d..689aa6c 100644
--- a/gpu/config/gpu_control_list_format.txt
+++ b/gpu/config/gpu_control_list_format.txt
@@ -67,6 +67,10 @@
 // 28. "test_group" is an non-negative integer. If not specified, it defaults
 //     to 0, which is Chrome's blacklist. Any entries with a non-zero test_group
 //     ID will be applied if Chrome runs with --gpu-blacklist-test-group=ID.
+// 29. "gpu_series" is a list of gpu series names. Currently supported series
+//     include: "intel_ivybridge", "intel_sandybridge", "intel_haswell",
+//     "intel_broadwell", "intel_skylake", "intel_kabylake", "intel_coffeelake",
+//     "intel_cherryview".
 //
 // VERSION includes "op", "style", "value", and "value2".  "op" can be any of
 // the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
diff --git a/gpu/config/gpu_control_list_testing.json b/gpu/config/gpu_control_list_testing.json
index 827ca7a4..81629662 100644
--- a/gpu/config/gpu_control_list_testing.json
+++ b/gpu/config/gpu_control_list_testing.json
@@ -733,6 +733,75 @@
       "features": [
         "test_feature_1"
       ]
+    },
+    {
+      "id": 61,
+      "description": "GpuControlListEntryTest.GpuSeries",
+      "gpu_series": [
+        "intel_skylake",
+        "intel_kabylake"
+      ],
+      "features": [
+        "test_feature_0"
+      ]
+    },
+    {
+      "id": 62,
+      "description": "GpuControlListEntryTest.GpuSeriesActive",
+      "gpu_series": [
+        "intel_kabylake"
+      ],
+      "multi_gpu_category": "active",
+      "features": [
+        "test_feature_0"
+      ]
+    },
+    {
+      "id": 63,
+      "description": "GpuControlListEntryTest.GpuSeriesAny",
+      "gpu_series": [
+        "intel_kabylake"
+      ],
+      "multi_gpu_category": "any",
+      "features": [
+        "test_feature_0"
+      ]
+    },
+    {
+      "id": 64,
+      "description": "GpuControlListEntryTest.GpuSeriesPrimary",
+      "gpu_series": [
+        "intel_kabylake"
+      ],
+      "multi_gpu_category": "primary",
+      "features": [
+        "test_feature_0"
+      ]
+    },
+    {
+      "id": 65,
+      "description": "GpuControlListEntryTest.GpuSeriesSecondary",
+      "gpu_series": [
+        "intel_kabylake"
+      ],
+      "multi_gpu_category": "secondary",
+      "features": [
+        "test_feature_0"
+      ]
+    },
+    {
+      "id": 66,
+      "description": "GpuControlListEntryTest.GpuSeriesInException",
+      "exceptions": [
+        {
+          "gpu_series": [
+            "intel_kabylake"
+          ]
+        }
+      ],
+      "features": [
+        "test_feature_0"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h b/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
index 60382f3..f41be06 100644
--- a/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
+++ b/gpu/config/gpu_control_list_testing_arrays_and_structs_autogen.h
@@ -267,8 +267,8 @@
 };
 
 const GpuControlList::MachineModelInfo kMachineModelInfoForEntry27 = {
-    arraysize(kMachineModelNameForEntry27),  // machine model name size
-    kMachineModelNameForEntry27,             // machine model names
+    base::size(kMachineModelNameForEntry27),  // machine model name size
+    kMachineModelNameForEntry27,              // machine model names
     {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
      nullptr},  // machine model version
 };
@@ -282,7 +282,7 @@
 };
 
 const GpuControlList::MachineModelInfo kMachineModelInfoForEntry28Exception0 = {
-    arraysize(
+    base::size(
         kMachineModelNameForEntry28Exception0),  // machine model name size
     kMachineModelNameForEntry28Exception0,       // machine model names
     {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
@@ -298,8 +298,8 @@
 };
 
 const GpuControlList::MachineModelInfo kMachineModelInfoForEntry29 = {
-    arraysize(kMachineModelNameForEntry29),  // machine model name size
-    kMachineModelNameForEntry29,             // machine model names
+    base::size(kMachineModelNameForEntry29),  // machine model name size
+    kMachineModelNameForEntry29,              // machine model names
     {GpuControlList::kEQ, GpuControlList::kVersionStyleNumerical, "7.1",
      nullptr},  // machine model version
 };
@@ -313,8 +313,8 @@
 };
 
 const GpuControlList::MachineModelInfo kMachineModelInfoForEntry30 = {
-    arraysize(kMachineModelNameForEntry30),  // machine model name size
-    kMachineModelNameForEntry30,             // machine model names
+    base::size(kMachineModelNameForEntry30),  // machine model name size
+    kMachineModelNameForEntry30,              // machine model names
     {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical, nullptr,
      nullptr},  // machine model version
 };
@@ -596,6 +596,55 @@
     2,          // test_group
 };
 
+const int kFeatureListForEntry61[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry61[2] = {
+    GpuControlList::GpuSeriesType::kIntelSkyLake,
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
+const int kFeatureListForEntry62[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry62[1] = {
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
+const int kFeatureListForEntry63[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry63[1] = {
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
+const int kFeatureListForEntry64[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry64[1] = {
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
+const int kFeatureListForEntry65[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry65[1] = {
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
+const int kFeatureListForEntry66[1] = {
+    TEST_FEATURE_0,
+};
+
+const GpuControlList::GpuSeriesType kGpuSeriesForEntry66Exception0[1] = {
+    GpuControlList::GpuSeriesType::kIntelKabyLake,
+};
+
 }  // namespace gpu
 
 #endif  // GPU_CONFIG_GPU_CONTROL_LIST_TESTING_ARRAYS_AND_STRUCTS_AUTOGEN_H_
diff --git a/gpu/config/gpu_control_list_testing_autogen.cc b/gpu/config/gpu_control_list_testing_autogen.cc
index 72408ae..0fefefe 100644
--- a/gpu/config/gpu_control_list_testing_autogen.cc
+++ b/gpu/config/gpu_control_list_testing_autogen.cc
@@ -19,26 +19,28 @@
     {
         1,  // id
         "GpuControlListEntryTest.DetailedEntry",
-        arraysize(kFeatureListForEntry1),         // features size
-        kFeatureListForEntry1,                    // features
-        arraysize(kDisabledExtensionsForEntry1),  // DisabledExtensions size
-        kDisabledExtensionsForEntry1,             // DisabledExtensions
-        0,                            // DisabledWebGLExtensions size
-        nullptr,                      // DisabledWebGLExtensions
-        arraysize(kCrBugsForEntry1),  // CrBugs size
-        kCrBugsForEntry1,             // CrBugs
+        base::size(kFeatureListForEntry1),         // features size
+        kFeatureListForEntry1,                     // features
+        base::size(kDisabledExtensionsForEntry1),  // DisabledExtensions size
+        kDisabledExtensionsForEntry1,              // DisabledExtensions
+        0,                             // DisabledWebGLExtensions size
+        nullptr,                       // DisabledWebGLExtensions
+        base::size(kCrBugsForEntry1),  // CrBugs size
+        kCrBugsForEntry1,              // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kEQ, GpuControlList::kVersionStyleNumerical,
              "10.6.4", nullptr},                    // os_version
             0x10de,                                 // vendor_id
-            arraysize(kDeviceIDsForEntry1),         // DeviceIDs size
+            base::size(kDeviceIDsForEntry1),        // DeviceIDs size
             kDeviceIDsForEntry1,                    // DeviceIDs
             GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
             &kDriverInfoForEntry1,                  // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -47,14 +49,14 @@
     {
         2,  // id
         "GpuControlListEntryTest.VendorOnAllOsEntry",
-        arraysize(kFeatureListForEntry2),  // features size
-        kFeatureListForEntry2,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry2),  // features size
+        kFeatureListForEntry2,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -67,6 +69,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -75,14 +79,14 @@
     {
         3,  // id
         "GpuControlListEntryTest.VendorOnLinuxEntry",
-        arraysize(kFeatureListForEntry3),  // features size
-        kFeatureListForEntry3,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry3),  // features size
+        kFeatureListForEntry3,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -95,6 +99,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -103,14 +109,14 @@
     {
         4,  // id
         "GpuControlListEntryTest.AllExceptNVidiaOnLinuxEntry",
-        arraysize(kFeatureListForEntry4),  // features size
-        kFeatureListForEntry4,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry4),  // features size
+        kFeatureListForEntry4,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -123,22 +129,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry4),  // exceptions count
-        kExceptionsForEntry4,             // exceptions
+        base::size(kExceptionsForEntry4),  // exceptions count
+        kExceptionsForEntry4,              // exceptions
     },
     {
         5,  // id
         "GpuControlListEntryTest.AllExceptIntelOnLinuxEntry",
-        arraysize(kFeatureListForEntry5),  // features size
-        kFeatureListForEntry5,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry5),  // features size
+        kFeatureListForEntry5,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -151,22 +159,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry5),  // exceptions count
-        kExceptionsForEntry5,             // exceptions
+        base::size(kExceptionsForEntry5),  // exceptions count
+        kExceptionsForEntry5,              // exceptions
     },
     {
         6,  // id
         "GpuControlListEntryTest.DateOnWindowsEntry",
-        arraysize(kFeatureListForEntry6),  // features size
-        kFeatureListForEntry6,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry6),  // features size
+        kFeatureListForEntry6,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -179,6 +189,8 @@
             &kDriverInfoForEntry6,                  // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -187,26 +199,28 @@
     {
         7,  // id
         "GpuControlListEntryTest.MultipleDevicesEntry",
-        arraysize(kFeatureListForEntry7),  // features size
-        kFeatureListForEntry7,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry7),  // features size
+        kFeatureListForEntry7,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                     // os_version
             0x10de,                                 // vendor_id
-            arraysize(kDeviceIDsForEntry7),         // DeviceIDs size
+            base::size(kDeviceIDsForEntry7),        // DeviceIDs size
             kDeviceIDsForEntry7,                    // DeviceIDs
             GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -215,14 +229,14 @@
     {
         8,  // id
         "GpuControlListEntryTest.ChromeOSEntry",
-        arraysize(kFeatureListForEntry8),  // features size
-        kFeatureListForEntry8,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry8),  // features size
+        kFeatureListForEntry8,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsChromeOS,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -235,6 +249,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -243,14 +259,14 @@
     {
         9,  // id
         "GpuControlListEntryTest.GlVersionGLESEntry",
-        arraysize(kFeatureListForEntry9),  // features size
-        kFeatureListForEntry9,             // features
-        0,                                 // DisabledExtensions size
-        nullptr,                           // DisabledExtensions
-        0,                                 // DisabledWebGLExtensions size
-        nullptr,                           // DisabledWebGLExtensions
-        0,                                 // CrBugs size
-        nullptr,                           // CrBugs
+        base::size(kFeatureListForEntry9),  // features size
+        kFeatureListForEntry9,              // features
+        0,                                  // DisabledExtensions size
+        nullptr,                            // DisabledExtensions
+        0,                                  // DisabledWebGLExtensions size
+        nullptr,                            // DisabledWebGLExtensions
+        0,                                  // CrBugs size
+        nullptr,                            // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -263,6 +279,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry9,                        // more data
         },
         0,        // exceptions count
@@ -271,14 +289,14 @@
     {
         10,  // id
         "GpuControlListEntryTest.GlVersionANGLEEntry",
-        arraysize(kFeatureListForEntry10),  // features size
-        kFeatureListForEntry10,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry10),  // features size
+        kFeatureListForEntry10,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -291,6 +309,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry10,                       // more data
         },
         0,        // exceptions count
@@ -299,14 +319,14 @@
     {
         11,  // id
         "GpuControlListEntryTest.GlVersionGLEntry",
-        arraysize(kFeatureListForEntry11),  // features size
-        kFeatureListForEntry11,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry11),  // features size
+        kFeatureListForEntry11,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -319,6 +339,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry11,                       // more data
         },
         0,        // exceptions count
@@ -327,14 +349,14 @@
     {
         12,  // id
         "GpuControlListEntryTest.GlVendorEqual",
-        arraysize(kFeatureListForEntry12),  // features size
-        kFeatureListForEntry12,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry12),  // features size
+        kFeatureListForEntry12,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -347,6 +369,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry12,                  // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -355,14 +379,14 @@
     {
         13,  // id
         "GpuControlListEntryTest.GlVendorWithDot",
-        arraysize(kFeatureListForEntry13),  // features size
-        kFeatureListForEntry13,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry13),  // features size
+        kFeatureListForEntry13,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -375,6 +399,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry13,                  // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -383,14 +409,14 @@
     {
         14,  // id
         "GpuControlListEntryTest.GlRendererContains",
-        arraysize(kFeatureListForEntry14),  // features size
-        kFeatureListForEntry14,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry14),  // features size
+        kFeatureListForEntry14,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -403,6 +429,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry14,                  // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -411,14 +439,14 @@
     {
         15,  // id
         "GpuControlListEntryTest.GlRendererCaseInsensitive",
-        arraysize(kFeatureListForEntry15),  // features size
-        kFeatureListForEntry15,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry15),  // features size
+        kFeatureListForEntry15,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -431,6 +459,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry15,                  // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -439,14 +469,14 @@
     {
         16,  // id
         "GpuControlListEntryTest.GlExtensionsEndWith",
-        arraysize(kFeatureListForEntry16),  // features size
-        kFeatureListForEntry16,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry16),  // features size
+        kFeatureListForEntry16,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -459,6 +489,8 @@
             nullptr,                                // driver info
             &kGLStringsForEntry16,                  // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -467,14 +499,14 @@
     {
         17,  // id
         "GpuControlListEntryTest.OptimusEntry",
-        arraysize(kFeatureListForEntry17),  // features size
-        kFeatureListForEntry17,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry17),  // features size
+        kFeatureListForEntry17,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -487,6 +519,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -495,14 +529,14 @@
     {
         18,  // id
         "GpuControlListEntryTest.AMDSwitchableEntry",
-        arraysize(kFeatureListForEntry18),  // features size
-        kFeatureListForEntry18,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry18),  // features size
+        kFeatureListForEntry18,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -515,6 +549,8 @@
             nullptr,                                      // driver info
             nullptr,                                      // GL strings
             nullptr,                                      // machine model info
+            0,                                            // gpu_series size
+            nullptr,                                      // gpu_series
             nullptr,                                      // more conditions
         },
         0,        // exceptions count
@@ -523,14 +559,14 @@
     {
         19,  // id
         "GpuControlListEntryTest.DriverVendorBeginWith",
-        arraysize(kFeatureListForEntry19),  // features size
-        kFeatureListForEntry19,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry19),  // features size
+        kFeatureListForEntry19,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -543,6 +579,8 @@
             &kDriverInfoForEntry19,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -551,14 +589,14 @@
     {
         20,  // id
         "GpuControlListEntryTest.LexicalDriverVersionEntry",
-        arraysize(kFeatureListForEntry20),  // features size
-        kFeatureListForEntry20,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry20),  // features size
+        kFeatureListForEntry20,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -571,6 +609,8 @@
             &kDriverInfoForEntry20,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -579,14 +619,14 @@
     {
         21,  // id
         "GpuControlListEntryTest.NeedsMoreInfoEntry",
-        arraysize(kFeatureListForEntry21),  // features size
-        kFeatureListForEntry21,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry21),  // features size
+        kFeatureListForEntry21,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -599,6 +639,8 @@
             &kDriverInfoForEntry21,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -607,14 +649,14 @@
     {
         22,  // id
         "GpuControlListEntryTest.NeedsMoreInfoForExceptionsEntry",
-        arraysize(kFeatureListForEntry22),  // features size
-        kFeatureListForEntry22,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry22),  // features size
+        kFeatureListForEntry22,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -627,22 +669,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry22),  // exceptions count
-        kExceptionsForEntry22,             // exceptions
+        base::size(kExceptionsForEntry22),  // exceptions count
+        kExceptionsForEntry22,              // exceptions
     },
     {
         23,  // id
         "GpuControlListEntryTest.NeedsMoreInfoForGlVersionEntry",
-        arraysize(kFeatureListForEntry23),  // features size
-        kFeatureListForEntry23,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry23),  // features size
+        kFeatureListForEntry23,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -655,6 +699,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry23,                       // more data
         },
         0,        // exceptions count
@@ -663,14 +709,14 @@
     {
         24,  // id
         "GpuControlListEntryTest.FeatureTypeAllEntry",
-        arraysize(kFeatureListForEntry24),  // features size
-        kFeatureListForEntry24,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry24),  // features size
+        kFeatureListForEntry24,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -683,6 +729,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -691,14 +739,14 @@
     {
         25,  // id
         "GpuControlListEntryTest.FeatureTypeAllEntryWithExceptions",
-        arraysize(kFeatureListForEntry25),  // features size
-        kFeatureListForEntry25,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry25),  // features size
+        kFeatureListForEntry25,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -711,6 +759,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -719,26 +769,28 @@
     {
         26,  // id
         "GpuControlListEntryTest.SingleActiveGPU",
-        arraysize(kFeatureListForEntry26),  // features size
-        kFeatureListForEntry26,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry26),  // features size
+        kFeatureListForEntry26,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                       // os_version
             0x10de,                                   // vendor_id
-            arraysize(kDeviceIDsForEntry26),          // DeviceIDs size
+            base::size(kDeviceIDsForEntry26),         // DeviceIDs size
             kDeviceIDsForEntry26,                     // DeviceIDs
             GpuControlList::kMultiGpuCategoryActive,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,       // multi_gpu_style
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -747,14 +799,14 @@
     {
         27,  // id
         "GpuControlListEntryTest.MachineModelName",
-        arraysize(kFeatureListForEntry27),  // features size
-        kFeatureListForEntry27,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry27),  // features size
+        kFeatureListForEntry27,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAndroid,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -767,6 +819,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             &kMachineModelInfoForEntry27,           // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -775,14 +829,14 @@
     {
         28,  // id
         "GpuControlListEntryTest.MachineModelNameException",
-        arraysize(kFeatureListForEntry28),  // features size
-        kFeatureListForEntry28,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry28),  // features size
+        kFeatureListForEntry28,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -795,22 +849,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry28),  // exceptions count
-        kExceptionsForEntry28,             // exceptions
+        base::size(kExceptionsForEntry28),  // exceptions count
+        kExceptionsForEntry28,              // exceptions
     },
     {
         29,  // id
         "GpuControlListEntryTest.MachineModelVersion",
-        arraysize(kFeatureListForEntry29),  // features size
-        kFeatureListForEntry29,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry29),  // features size
+        kFeatureListForEntry29,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -823,6 +879,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             &kMachineModelInfoForEntry29,           // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -831,14 +889,14 @@
     {
         30,  // id
         "GpuControlListEntryTest.MachineModelVersionException",
-        arraysize(kFeatureListForEntry30),  // features size
-        kFeatureListForEntry30,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry30),  // features size
+        kFeatureListForEntry30,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -851,34 +909,38 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             &kMachineModelInfoForEntry30,           // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry30),  // exceptions count
-        kExceptionsForEntry30,             // exceptions
+        base::size(kExceptionsForEntry30),  // exceptions count
+        kExceptionsForEntry30,              // exceptions
     },
     {
         31,  // id
         "GpuControlListEntryDualGPUTest.CategoryAny.Intel",
-        arraysize(kFeatureListForEntry31),  // features size
-        kFeatureListForEntry31,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry31),  // features size
+        kFeatureListForEntry31,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                    // os_version
             0x8086,                                // vendor_id
-            arraysize(kDeviceIDsForEntry31),       // DeviceIDs size
+            base::size(kDeviceIDsForEntry31),      // DeviceIDs size
             kDeviceIDsForEntry31,                  // DeviceIDs
             GpuControlList::kMultiGpuCategoryAny,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,    // multi_gpu_style
             nullptr,                               // driver info
             nullptr,                               // GL strings
             nullptr,                               // machine model info
+            0,                                     // gpu_series size
+            nullptr,                               // gpu_series
             nullptr,                               // more conditions
         },
         0,        // exceptions count
@@ -887,26 +949,28 @@
     {
         32,  // id
         "GpuControlListEntryDualGPUTest.CategoryAny.NVidia",
-        arraysize(kFeatureListForEntry32),  // features size
-        kFeatureListForEntry32,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry32),  // features size
+        kFeatureListForEntry32,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                    // os_version
             0x10de,                                // vendor_id
-            arraysize(kDeviceIDsForEntry32),       // DeviceIDs size
+            base::size(kDeviceIDsForEntry32),      // DeviceIDs size
             kDeviceIDsForEntry32,                  // DeviceIDs
             GpuControlList::kMultiGpuCategoryAny,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,    // multi_gpu_style
             nullptr,                               // driver info
             nullptr,                               // GL strings
             nullptr,                               // machine model info
+            0,                                     // gpu_series size
+            nullptr,                               // gpu_series
             nullptr,                               // more conditions
         },
         0,        // exceptions count
@@ -915,26 +979,28 @@
     {
         33,  // id
         "GpuControlListEntryDualGPUTest.CategorySecondary",
-        arraysize(kFeatureListForEntry33),  // features size
-        kFeatureListForEntry33,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry33),  // features size
+        kFeatureListForEntry33,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                          // os_version
             0x8086,                                      // vendor_id
-            arraysize(kDeviceIDsForEntry33),             // DeviceIDs size
+            base::size(kDeviceIDsForEntry33),            // DeviceIDs size
             kDeviceIDsForEntry33,                        // DeviceIDs
             GpuControlList::kMultiGpuCategorySecondary,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,          // multi_gpu_style
             nullptr,                                     // driver info
             nullptr,                                     // GL strings
             nullptr,                                     // machine model info
+            0,                                           // gpu_series size
+            nullptr,                                     // gpu_series
             nullptr,                                     // more conditions
         },
         0,        // exceptions count
@@ -943,26 +1009,28 @@
     {
         34,  // id
         "GpuControlListEntryDualGPUTest.CategoryPrimary",
-        arraysize(kFeatureListForEntry34),  // features size
-        kFeatureListForEntry34,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry34),  // features size
+        kFeatureListForEntry34,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                        // os_version
             0x8086,                                    // vendor_id
-            arraysize(kDeviceIDsForEntry34),           // DeviceIDs size
+            base::size(kDeviceIDsForEntry34),          // DeviceIDs size
             kDeviceIDsForEntry34,                      // DeviceIDs
             GpuControlList::kMultiGpuCategoryPrimary,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,        // multi_gpu_style
             nullptr,                                   // driver info
             nullptr,                                   // GL strings
             nullptr,                                   // machine model info
+            0,                                         // gpu_series size
+            nullptr,                                   // gpu_series
             nullptr,                                   // more conditions
         },
         0,        // exceptions count
@@ -971,26 +1039,28 @@
     {
         35,  // id
         "GpuControlListEntryDualGPUTest.CategoryDefault",
-        arraysize(kFeatureListForEntry35),  // features size
-        kFeatureListForEntry35,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry35),  // features size
+        kFeatureListForEntry35,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                     // os_version
             0x8086,                                 // vendor_id
-            arraysize(kDeviceIDsForEntry35),        // DeviceIDs size
+            base::size(kDeviceIDsForEntry35),       // DeviceIDs size
             kDeviceIDsForEntry35,                   // DeviceIDs
             GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -999,26 +1069,28 @@
     {
         36,  // id
         "GpuControlListEntryDualGPUTest.ActiveSecondaryGPU",
-        arraysize(kFeatureListForEntry36),  // features size
-        kFeatureListForEntry36,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry36),  // features size
+        kFeatureListForEntry36,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                       // os_version
             0x8086,                                   // vendor_id
-            arraysize(kDeviceIDsForEntry36),          // DeviceIDs size
+            base::size(kDeviceIDsForEntry36),         // DeviceIDs size
             kDeviceIDsForEntry36,                     // DeviceIDs
             GpuControlList::kMultiGpuCategoryActive,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,       // multi_gpu_style
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -1027,14 +1099,14 @@
     {
         37,  // id
         "GpuControlListEntryDualGPUTest.VendorOnlyActiveSecondaryGPU",
-        arraysize(kFeatureListForEntry37),  // features size
-        kFeatureListForEntry37,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry37),  // features size
+        kFeatureListForEntry37,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1047,6 +1119,8 @@
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -1055,26 +1129,28 @@
     {
         38,  // id
         "GpuControlListEntryDualGPUTest.ActivePrimaryGPU",
-        arraysize(kFeatureListForEntry38),  // features size
-        kFeatureListForEntry38,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry38),  // features size
+        kFeatureListForEntry38,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
              nullptr, nullptr},                       // os_version
             0x10de,                                   // vendor_id
-            arraysize(kDeviceIDsForEntry38),          // DeviceIDs size
+            base::size(kDeviceIDsForEntry38),         // DeviceIDs size
             kDeviceIDsForEntry38,                     // DeviceIDs
             GpuControlList::kMultiGpuCategoryActive,  // multi_gpu_category
             GpuControlList::kMultiGpuStyleNone,       // multi_gpu_style
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -1083,14 +1159,14 @@
     {
         39,  // id
         "GpuControlListEntryDualGPUTest.VendorOnlyActivePrimaryGPU",
-        arraysize(kFeatureListForEntry39),  // features size
-        kFeatureListForEntry39,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry39),  // features size
+        kFeatureListForEntry39,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsMacosx,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1103,6 +1179,8 @@
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -1111,14 +1189,14 @@
     {
         40,  // id
         "GpuControlListEntryTest.PixelShaderVersion",
-        arraysize(kFeatureListForEntry40),  // features size
-        kFeatureListForEntry40,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry40),  // features size
+        kFeatureListForEntry40,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1131,6 +1209,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry40,                       // more data
         },
         0,        // exceptions count
@@ -1139,14 +1219,14 @@
     {
         41,  // id
         "GpuControlListEntryTest.OsVersionZeroLT",
-        arraysize(kFeatureListForEntry41),  // features size
-        kFeatureListForEntry41,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry41),  // features size
+        kFeatureListForEntry41,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAndroid,  // os_type
             {GpuControlList::kLT, GpuControlList::kVersionStyleNumerical, "4.2",
@@ -1159,6 +1239,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1167,14 +1249,14 @@
     {
         42,  // id
         "GpuControlListEntryTest.OsVersionZeroAny",
-        arraysize(kFeatureListForEntry42),  // features size
-        kFeatureListForEntry42,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry42),  // features size
+        kFeatureListForEntry42,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAndroid,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1187,6 +1269,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1195,14 +1279,14 @@
     {
         43,  // id
         "GpuControlListEntryTest.OsComparisonAny",
-        arraysize(kFeatureListForEntry43),  // features size
-        kFeatureListForEntry43,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry43),  // features size
+        kFeatureListForEntry43,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1215,6 +1299,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1223,14 +1309,14 @@
     {
         44,  // id
         "GpuControlListEntryTest.OsComparisonGE",
-        arraysize(kFeatureListForEntry44),  // features size
-        kFeatureListForEntry44,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry44),  // features size
+        kFeatureListForEntry44,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kGE, GpuControlList::kVersionStyleNumerical, "6",
@@ -1243,6 +1329,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1251,14 +1339,14 @@
     {
         45,  // id
         "GpuControlListEntryTest.ExceptionWithoutVendorId",
-        arraysize(kFeatureListForEntry45),  // features size
-        kFeatureListForEntry45,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry45),  // features size
+        kFeatureListForEntry45,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1271,22 +1359,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry45),  // exceptions count
-        kExceptionsForEntry45,             // exceptions
+        base::size(kExceptionsForEntry45),  // exceptions count
+        kExceptionsForEntry45,              // exceptions
     },
     {
         46,  // id
         "GpuControlListEntryTest.MultiGpuStyleAMDSwitchableDiscrete",
-        arraysize(kFeatureListForEntry46),  // features size
-        kFeatureListForEntry46,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry46),  // features size
+        kFeatureListForEntry46,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1300,6 +1390,8 @@
             nullptr,                                  // driver info
             nullptr,                                  // GL strings
             nullptr,                                  // machine model info
+            0,                                        // gpu_series size
+            nullptr,                                  // gpu_series
             nullptr,                                  // more conditions
         },
         0,        // exceptions count
@@ -1308,14 +1400,14 @@
     {
         47,  // id
         "GpuControlListEntryTest.MultiGpuStyleAMDSwitchableIntegrated",
-        arraysize(kFeatureListForEntry47),  // features size
-        kFeatureListForEntry47,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry47),  // features size
+        kFeatureListForEntry47,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1329,6 +1421,8 @@
             nullptr,                                    // driver info
             nullptr,                                    // GL strings
             nullptr,                                    // machine model info
+            0,                                          // gpu_series size
+            nullptr,                                    // gpu_series
             nullptr,                                    // more conditions
         },
         0,        // exceptions count
@@ -1337,14 +1431,14 @@
     {
         48,  // id
         "GpuControlListEntryTest.InProcessGPU",
-        arraysize(kFeatureListForEntry48),  // features size
-        kFeatureListForEntry48,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry48),  // features size
+        kFeatureListForEntry48,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1357,6 +1451,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry48,                       // more data
         },
         0,        // exceptions count
@@ -1365,14 +1461,14 @@
     {
         49,  // id
         "GpuControlListEntryTest.SameGPUTwiceTest",
-        arraysize(kFeatureListForEntry49),  // features size
-        kFeatureListForEntry49,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry49),  // features size
+        kFeatureListForEntry49,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1385,6 +1481,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1393,14 +1491,14 @@
     {
         50,  // id
         "GpuControlListEntryTest.NVidiaNumberingScheme",
-        arraysize(kFeatureListForEntry50),  // features size
-        kFeatureListForEntry50,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry50),  // features size
+        kFeatureListForEntry50,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1413,6 +1511,8 @@
             &kDriverInfoForEntry50,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1421,14 +1521,14 @@
     {
         51,  // id
         "GpuControlListTest.NeedsMoreInfo",
-        arraysize(kFeatureListForEntry51),  // features size
-        kFeatureListForEntry51,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry51),  // features size
+        kFeatureListForEntry51,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsWin,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1441,6 +1541,8 @@
             &kDriverInfoForEntry51,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1449,14 +1551,14 @@
     {
         52,  // id
         "GpuControlListTest.NeedsMoreInfoForExceptions",
-        arraysize(kFeatureListForEntry52),  // features size
-        kFeatureListForEntry52,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry52),  // features size
+        kFeatureListForEntry52,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1469,22 +1571,24 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
-        arraysize(kExceptionsForEntry52),  // exceptions count
-        kExceptionsForEntry52,             // exceptions
+        base::size(kExceptionsForEntry52),  // exceptions count
+        kExceptionsForEntry52,              // exceptions
     },
     {
         53,  // id
         "GpuControlListTest.IgnorableEntries.0",
-        arraysize(kFeatureListForEntry53),  // features size
-        kFeatureListForEntry53,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry53),  // features size
+        kFeatureListForEntry53,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1497,6 +1601,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1505,14 +1611,14 @@
     {
         54,  // id
         "GpuControlListTest.IgnorableEntries.1",
-        arraysize(kFeatureListForEntry54),  // features size
-        kFeatureListForEntry54,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry54),  // features size
+        kFeatureListForEntry54,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1525,6 +1631,8 @@
             &kDriverInfoForEntry54,                 // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1533,10 +1641,10 @@
     {
         55,  // id
         "GpuControlListTest.DisabledExtensionTest.0",
-        0,                                         // feature size
-        nullptr,                                   // features
-        arraysize(kDisabledExtensionsForEntry55),  // DisabledExtensions size
-        kDisabledExtensionsForEntry55,             // DisabledExtensions
+        0,                                          // feature size
+        nullptr,                                    // features
+        base::size(kDisabledExtensionsForEntry55),  // DisabledExtensions size
+        kDisabledExtensionsForEntry55,              // DisabledExtensions
         0,        // DisabledWebGLExtensions size
         nullptr,  // DisabledWebGLExtensions
         0,        // CrBugs size
@@ -1553,6 +1661,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1561,10 +1671,10 @@
     {
         56,  // id
         "GpuControlListTest.DisabledExtensionTest.1",
-        0,                                         // feature size
-        nullptr,                                   // features
-        arraysize(kDisabledExtensionsForEntry56),  // DisabledExtensions size
-        kDisabledExtensionsForEntry56,             // DisabledExtensions
+        0,                                          // feature size
+        nullptr,                                    // features
+        base::size(kDisabledExtensionsForEntry56),  // DisabledExtensions size
+        kDisabledExtensionsForEntry56,              // DisabledExtensions
         0,        // DisabledWebGLExtensions size
         nullptr,  // DisabledWebGLExtensions
         0,        // CrBugs size
@@ -1581,6 +1691,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1589,14 +1701,14 @@
     {
         57,  // id
         "GpuControlListEntryTest.DirectRendering",
-        arraysize(kFeatureListForEntry57),  // features size
-        kFeatureListForEntry57,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry57),  // features size
+        kFeatureListForEntry57,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1609,6 +1721,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry57,                       // more data
         },
         0,        // exceptions count
@@ -1617,14 +1731,14 @@
     {
         58,  // id
         "GpuControlListTest.LinuxKernelVersion",
-        arraysize(kFeatureListForEntry58),  // features size
-        kFeatureListForEntry58,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry58),  // features size
+        kFeatureListForEntry58,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsLinux,  // os_type
             {GpuControlList::kLT, GpuControlList::kVersionStyleNumerical,
@@ -1637,6 +1751,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             nullptr,                                // more conditions
         },
         0,        // exceptions count
@@ -1645,14 +1761,14 @@
     {
         59,  // id
         "GpuControlListTest.TestGroup.0",
-        arraysize(kFeatureListForEntry59),  // features size
-        kFeatureListForEntry59,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry59),  // features size
+        kFeatureListForEntry59,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1665,6 +1781,8 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry59,                       // more data
         },
         0,        // exceptions count
@@ -1673,14 +1791,14 @@
     {
         60,  // id
         "GpuControlListTest.TestGroup.1",
-        arraysize(kFeatureListForEntry60),  // features size
-        kFeatureListForEntry60,             // features
-        0,                                  // DisabledExtensions size
-        nullptr,                            // DisabledExtensions
-        0,                                  // DisabledWebGLExtensions size
-        nullptr,                            // DisabledWebGLExtensions
-        0,                                  // CrBugs size
-        nullptr,                            // CrBugs
+        base::size(kFeatureListForEntry60),  // features size
+        kFeatureListForEntry60,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
         {
             GpuControlList::kOsAny,  // os_type
             {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
@@ -1693,11 +1811,193 @@
             nullptr,                                // driver info
             nullptr,                                // GL strings
             nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
             &kMoreForEntry60,                       // more data
         },
         0,        // exceptions count
         nullptr,  // exceptions
     },
+    {
+        61,  // id
+        "GpuControlListEntryTest.GpuSeries",
+        base::size(kFeatureListForEntry61),  // features size
+        kFeatureListForEntry61,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                     // os_version
+            0x00,                                   // vendor_id
+            0,                                      // DeviceIDs size
+            nullptr,                                // DeviceIDs
+            GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
+            nullptr,                                // driver info
+            nullptr,                                // GL strings
+            nullptr,                                // machine model info
+            base::size(kGpuSeriesForEntry61),       // gpu_series size
+            kGpuSeriesForEntry61,                   // gpu_series
+            nullptr,                                // more conditions
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
+    {
+        62,  // id
+        "GpuControlListEntryTest.GpuSeriesActive",
+        base::size(kFeatureListForEntry62),  // features size
+        kFeatureListForEntry62,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                       // os_version
+            0x00,                                     // vendor_id
+            0,                                        // DeviceIDs size
+            nullptr,                                  // DeviceIDs
+            GpuControlList::kMultiGpuCategoryActive,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,       // multi_gpu_style
+            nullptr,                                  // driver info
+            nullptr,                                  // GL strings
+            nullptr,                                  // machine model info
+            base::size(kGpuSeriesForEntry62),         // gpu_series size
+            kGpuSeriesForEntry62,                     // gpu_series
+            nullptr,                                  // more conditions
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
+    {
+        63,  // id
+        "GpuControlListEntryTest.GpuSeriesAny",
+        base::size(kFeatureListForEntry63),  // features size
+        kFeatureListForEntry63,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                    // os_version
+            0x00,                                  // vendor_id
+            0,                                     // DeviceIDs size
+            nullptr,                               // DeviceIDs
+            GpuControlList::kMultiGpuCategoryAny,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,    // multi_gpu_style
+            nullptr,                               // driver info
+            nullptr,                               // GL strings
+            nullptr,                               // machine model info
+            base::size(kGpuSeriesForEntry63),      // gpu_series size
+            kGpuSeriesForEntry63,                  // gpu_series
+            nullptr,                               // more conditions
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
+    {
+        64,  // id
+        "GpuControlListEntryTest.GpuSeriesPrimary",
+        base::size(kFeatureListForEntry64),  // features size
+        kFeatureListForEntry64,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                        // os_version
+            0x00,                                      // vendor_id
+            0,                                         // DeviceIDs size
+            nullptr,                                   // DeviceIDs
+            GpuControlList::kMultiGpuCategoryPrimary,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,        // multi_gpu_style
+            nullptr,                                   // driver info
+            nullptr,                                   // GL strings
+            nullptr,                                   // machine model info
+            base::size(kGpuSeriesForEntry64),          // gpu_series size
+            kGpuSeriesForEntry64,                      // gpu_series
+            nullptr,                                   // more conditions
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
+    {
+        65,  // id
+        "GpuControlListEntryTest.GpuSeriesSecondary",
+        base::size(kFeatureListForEntry65),  // features size
+        kFeatureListForEntry65,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                          // os_version
+            0x00,                                        // vendor_id
+            0,                                           // DeviceIDs size
+            nullptr,                                     // DeviceIDs
+            GpuControlList::kMultiGpuCategorySecondary,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,          // multi_gpu_style
+            nullptr,                                     // driver info
+            nullptr,                                     // GL strings
+            nullptr,                                     // machine model info
+            base::size(kGpuSeriesForEntry65),            // gpu_series size
+            kGpuSeriesForEntry65,                        // gpu_series
+            nullptr,                                     // more conditions
+        },
+        0,        // exceptions count
+        nullptr,  // exceptions
+    },
+    {
+        66,  // id
+        "GpuControlListEntryTest.GpuSeriesInException",
+        base::size(kFeatureListForEntry66),  // features size
+        kFeatureListForEntry66,              // features
+        0,                                   // DisabledExtensions size
+        nullptr,                             // DisabledExtensions
+        0,                                   // DisabledWebGLExtensions size
+        nullptr,                             // DisabledWebGLExtensions
+        0,                                   // CrBugs size
+        nullptr,                             // CrBugs
+        {
+            GpuControlList::kOsAny,  // os_type
+            {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+             nullptr, nullptr},                     // os_version
+            0x00,                                   // vendor_id
+            0,                                      // DeviceIDs size
+            nullptr,                                // DeviceIDs
+            GpuControlList::kMultiGpuCategoryNone,  // multi_gpu_category
+            GpuControlList::kMultiGpuStyleNone,     // multi_gpu_style
+            nullptr,                                // driver info
+            nullptr,                                // GL strings
+            nullptr,                                // machine model info
+            0,                                      // gpu_series size
+            nullptr,                                // gpu_series
+            nullptr,                                // more conditions
+        },
+        base::size(kExceptionsForEntry66),  // exceptions count
+        kExceptionsForEntry66,              // exceptions
+    },
 };
-const size_t kGpuControlListTestingEntryCount = 60;
+const size_t kGpuControlListTestingEntryCount = 66;
 }  // namespace gpu
diff --git a/gpu/config/gpu_control_list_testing_entry_enums_autogen.h b/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
index a6af543b..3af233e 100644
--- a/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
+++ b/gpu/config/gpu_control_list_testing_entry_enums_autogen.h
@@ -73,6 +73,12 @@
   kGpuControlListTest_LinuxKernelVersion = 57,
   kGpuControlListTest_TestGroup_0 = 58,
   kGpuControlListTest_TestGroup_1 = 59,
+  kGpuControlListEntryTest_GpuSeries = 60,
+  kGpuControlListEntryTest_GpuSeriesActive = 61,
+  kGpuControlListEntryTest_GpuSeriesAny = 62,
+  kGpuControlListEntryTest_GpuSeriesPrimary = 63,
+  kGpuControlListEntryTest_GpuSeriesSecondary = 64,
+  kGpuControlListEntryTest_GpuSeriesInException = 65,
 };
 }  // namespace gpu
 
diff --git a/gpu/config/gpu_control_list_testing_exceptions_autogen.h b/gpu/config/gpu_control_list_testing_exceptions_autogen.h
index b1590ae..76c9089 100644
--- a/gpu/config/gpu_control_list_testing_exceptions_autogen.h
+++ b/gpu/config/gpu_control_list_testing_exceptions_autogen.h
@@ -25,6 +25,8 @@
         nullptr,                                // driver info
         nullptr,                                // GL strings
         nullptr,                                // machine model info
+        0,                                      // gpu_series size
+        nullptr,                                // gpu_series
         nullptr,                                // more conditions
     },
 };
@@ -42,6 +44,8 @@
         nullptr,                                // driver info
         nullptr,                                // GL strings
         nullptr,                                // machine model info
+        0,                                      // gpu_series size
+        nullptr,                                // gpu_series
         nullptr,                                // more conditions
     },
 };
@@ -59,6 +63,8 @@
         nullptr,                                // driver info
         &kGLStringsForEntry22Exception0,        // GL strings
         nullptr,                                // machine model info
+        0,                                      // gpu_series size
+        nullptr,                                // gpu_series
         nullptr,                                // more conditions
     },
 };
@@ -76,6 +82,8 @@
         nullptr,                                 // driver info
         nullptr,                                 // GL strings
         &kMachineModelInfoForEntry28Exception0,  // machine model info
+        0,                                       // gpu_series size
+        nullptr,                                 // gpu_series
         nullptr,                                 // more conditions
     },
 };
@@ -93,6 +101,8 @@
         nullptr,                                 // driver info
         nullptr,                                 // GL strings
         &kMachineModelInfoForEntry30Exception0,  // machine model info
+        0,                                       // gpu_series size
+        nullptr,                                 // gpu_series
         nullptr,                                 // more conditions
     },
 };
@@ -101,30 +111,34 @@
     {
         GpuControlList::kOsAny,  // os_type
         {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
-         nullptr, nullptr},                         // os_version
-        0x8086,                                     // vendor_id
-        arraysize(kDeviceIDsForEntry45Exception0),  // DeviceIDs size
-        kDeviceIDsForEntry45Exception0,             // DeviceIDs
-        GpuControlList::kMultiGpuCategoryNone,      // multi_gpu_category
-        GpuControlList::kMultiGpuStyleNone,         // multi_gpu_style
-        &kDriverInfoForEntry45Exception0,           // driver info
-        nullptr,                                    // GL strings
-        nullptr,                                    // machine model info
-        nullptr,                                    // more conditions
+         nullptr, nullptr},                          // os_version
+        0x8086,                                      // vendor_id
+        base::size(kDeviceIDsForEntry45Exception0),  // DeviceIDs size
+        kDeviceIDsForEntry45Exception0,              // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,       // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,          // multi_gpu_style
+        &kDriverInfoForEntry45Exception0,            // driver info
+        nullptr,                                     // GL strings
+        nullptr,                                     // machine model info
+        0,                                           // gpu_series size
+        nullptr,                                     // gpu_series
+        nullptr,                                     // more conditions
     },
     {
         GpuControlList::kOsAny,  // os_type
         {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
-         nullptr, nullptr},                         // os_version
-        0x8086,                                     // vendor_id
-        arraysize(kDeviceIDsForEntry45Exception1),  // DeviceIDs size
-        kDeviceIDsForEntry45Exception1,             // DeviceIDs
-        GpuControlList::kMultiGpuCategoryNone,      // multi_gpu_category
-        GpuControlList::kMultiGpuStyleNone,         // multi_gpu_style
-        &kDriverInfoForEntry45Exception1,           // driver info
-        nullptr,                                    // GL strings
-        nullptr,                                    // machine model info
-        nullptr,                                    // more conditions
+         nullptr, nullptr},                          // os_version
+        0x8086,                                      // vendor_id
+        base::size(kDeviceIDsForEntry45Exception1),  // DeviceIDs size
+        kDeviceIDsForEntry45Exception1,              // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,       // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,          // multi_gpu_style
+        &kDriverInfoForEntry45Exception1,            // driver info
+        nullptr,                                     // GL strings
+        nullptr,                                     // machine model info
+        0,                                           // gpu_series size
+        nullptr,                                     // gpu_series
+        nullptr,                                     // more conditions
     },
 };
 
@@ -141,10 +155,31 @@
         nullptr,                                // driver info
         &kGLStringsForEntry52Exception0,        // GL strings
         nullptr,                                // machine model info
+        0,                                      // gpu_series size
+        nullptr,                                // gpu_series
         nullptr,                                // more conditions
     },
 };
 
+const GpuControlList::Conditions kExceptionsForEntry66[1] = {
+    {
+        GpuControlList::kOsAny,  // os_type
+        {GpuControlList::kUnknown, GpuControlList::kVersionStyleNumerical,
+         nullptr, nullptr},                          // os_version
+        0x00,                                        // vendor_id
+        0,                                           // DeviceIDs size
+        nullptr,                                     // DeviceIDs
+        GpuControlList::kMultiGpuCategoryNone,       // multi_gpu_category
+        GpuControlList::kMultiGpuStyleNone,          // multi_gpu_style
+        nullptr,                                     // driver info
+        nullptr,                                     // GL strings
+        nullptr,                                     // machine model info
+        base::size(kGpuSeriesForEntry66Exception0),  // gpu_series size
+        kGpuSeriesForEntry66Exception0,              // gpu_series
+        nullptr,                                     // more conditions
+    },
+};
+
 }  // namespace gpu
 
 #endif  // GPU_CONFIG_GPU_CONTROL_LIST_TESTING_EXCEPTIONS_AUTOGEN_H_
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 1d3404c..a77c984b 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -1935,12 +1935,8 @@
           "value": "10.13.2"
         }
       },
-      "vendor_id": "0x8086",
-      "device_id" : [
-        "0x1902", "0x1906", "0x190A", "0x190B", "0x190E", "0x1912", "0x1913", "0x1915",
-        "0x1916", "0x1917", "0x191A", "0x191B", "0x191D", "0x191E", "0x1921", "0x1923",
-        "0x1926", "0x1927", "0x192A", "0x192B", "0x192D", "0x1932", "0x193A", "0x193B",
-        "0x193D"
+      "gpu_series" : [
+        "intel_skylake"
       ],
       "features" : [
         "emulate_isnan_on_float"
@@ -2367,17 +2363,11 @@
       "os": {
         "type": "win"
       },
-      "vendor_id": "0x8086",
-      "device_id": ["0x1602", "0x1606", "0x160a", "0x160b", "0x160d",
-                    "0x160e", "0x1612", "0x1616", "0x161a", "0x161b",
-                    "0x161d", "0x161e", "0x1622", "0x1626", "0x162a",
-                    "0x162b", "0x162d", "0x162e", "0x22b0", "0x22b1",
-                    "0x22b2", "0x22b3", "0x1902", "0x1906", "0x190a",
-                    "0x190b", "0x190e", "0x1912", "0x1913", "0x1915",
-                    "0x1916", "0x1917", "0x191a", "0x191b", "0x191d",
-                    "0x191e", "0x1921", "0x1923", "0x1926", "0x1927",
-                    "0x192a", "0x192b", "0x192d", "0x1932", "0x193a",
-                    "0x193b", "0x193d"],
+      "gpu_series": [
+        "intel_broadwell",
+        "intel_skylake",
+        "intel_cherryview"
+      ],
       "features": [
         "disable_accelerated_vpx_decode"
       ]
@@ -2608,10 +2598,11 @@
       "os": {
         "type": "macosx"
       },
-      "vendor_id": "0x8086",
-      "device_id": ["0x1927", "0x1926", "0x191e", "0x1916", "0x1912", "0x1912",
-                    "0x191b", "0x193b", "0x591e", "0x5926", "0x5927", "0x5912",
-                    "0x5916", "0x591b", "0x3e92"],
+      "gpu_series": [
+        "intel_skylake",
+        "intel_kabylake",
+        "intel_coffeelake"
+      ],
       "multi_gpu_category": "any",
       "features": [
         "flush_on_framebuffer_change"
diff --git a/gpu/config/process_json.py b/gpu/config/process_json.py
index 8465760..de09c6b 100755
--- a/gpu/config/process_json.py
+++ b/gpu/config/process_json.py
@@ -139,7 +139,7 @@
       data_helper_file.write(',\n')
     data_helper_file.write('};\n\n')
     # use the list
-    data_file.write('arraysize(%s),  // %s size\n' % (var_name, entry_kind))
+    data_file.write('base::size(%s),  // %s size\n' % (var_name, entry_kind))
     data_file.write('%s,  // %s\n' % (var_name, entry_kind))
   else:
     data_file.write('0,  // %s size\n' % entry_kind)
@@ -233,7 +233,7 @@
       data_helper_file.write(',\n')
     data_helper_file.write('};\n\n')
     # reference the list
-    data_file.write('arraysize(%s),  // %s size\n' % (var_name, name_tag))
+    data_file.write('base::size(%s),  // %s size\n' % (var_name, name_tag))
     data_file.write('%s,  // %s\n' % (var_name, name_tag))
   else:
     data_file.write('0,  // %s size\n' % name_tag)
@@ -281,7 +281,7 @@
     data_helper_file.write(
       'const GpuControlList::MachineModelInfo %s = {\n' % var_name)
     if machine_model_name:
-      data_helper_file.write('arraysize(%s),  // machine model name size\n' %
+      data_helper_file.write('base::size(%s),  // machine model name size\n' %
                              model_name_var_name)
       data_helper_file.write('%s,  // machine model names\n' %
                              model_name_var_name)
@@ -349,6 +349,7 @@
   device_id = None
   multi_gpu_category = ''
   multi_gpu_style = ''
+  gpu_series_list = None
   driver_vendor = ''
   driver_version = None
   driver_date = None
@@ -406,6 +407,8 @@
       multi_gpu_category = entry[key]
     elif key == 'multi_gpu_style':
       multi_gpu_style = entry[key]
+    elif key == 'gpu_series':
+      gpu_series_list = entry[key]
     elif key == 'driver_vendor':
       driver_vendor = entry[key]
     elif key == 'driver_version':
@@ -474,6 +477,8 @@
   write_machine_model_info(entry_id, is_exception, exception_id,
                            machine_model_name, machine_model_version,
                            data_file, data_helper_file)
+  write_gpu_series_list(entry_id, is_exception, exception_id, gpu_series_list,
+                        data_file, data_helper_file)
   # group a bunch of less used conditions
   if (gl_version != None or pixel_shader_version != None or in_process_gpu or
       gl_reset_notification_strategy != None or (not direct_rendering) or
@@ -486,6 +491,40 @@
     data_file.write('nullptr,  // more conditions\n')
 
 
+def write_gpu_series_list(entry_id, is_exception, exception_id, gpu_series_list,
+                          data_file, data_helper_file):
+  if gpu_series_list:
+    var_name = 'kGpuSeriesForEntry' + str(entry_id)
+    if is_exception:
+      var_name += 'Exception' + str(exception_id)
+    data_helper_file.write('const GpuControlList::GpuSeriesType %s[%d] = {\n' %
+                           (var_name, len(gpu_series_list)))
+    gpu_series_map = {
+      'intel_sandybridge': 'kIntelSandyBridge',
+      'intel_valleyview': 'kIntelValleyView',
+      'intel_ivybridge': 'kIntelIvyBridge',
+      'intel_haswell': 'kIntelHaswell',
+      'intel_cherryview': 'kIntelCherryView',
+      'intel_broadwell': 'kIntelBroadwell',
+      'intel_apollolake': 'kIntelApolloLake',
+      'intel_skylake': 'kIntelSkyLake',
+      'intel_geminilake': 'kIntelGeminiLake',
+      'intel_kabylake': 'kIntelKabyLake',
+      'intel_coffeelake': 'kIntelCoffeeLake',
+    }
+    for series in gpu_series_list:
+      assert gpu_series_map.has_key(series)
+      data_helper_file.write('GpuControlList::GpuSeriesType::%s,\n' %
+                             gpu_series_map[series])
+    data_helper_file.write('};\n\n')
+
+    data_file.write('base::size(%s),  // gpu_series size\n' % var_name)
+    data_file.write('%s,  // gpu_series\n' % var_name)
+  else:
+    data_file.write('0,  // gpu_series size\n')
+    data_file.write('nullptr,  // gpu_series\n')
+
+
 def write_entry_more_data(entry_id, is_exception, exception_id, gl_type,
                           gl_version, pixel_shader_version, in_process_gpu,
                           gl_reset_notification_strategy, direct_rendering,
@@ -522,7 +561,7 @@
   if 'features' in entry:
     features = entry['features']
     feature_set = get_feature_set(features, total_feature_set)
-    data_file.write('arraysize(kFeatureListForEntry%d),  // features size\n' %
+    data_file.write('base::size(kFeatureListForEntry%d),  // features size\n' %
                     entry_id)
     data_file.write('kFeatureListForEntry%d,  // features\n' % entry_id)
     write_features(entry_id, feature_set, feature_name_prefix, data_helper_file)
@@ -566,7 +605,7 @@
                        data_exception_file, data_helper_file, None)
       data_exception_file.write('},\n')
     data_exception_file.write('};\n\n')
-    data_file.write('arraysize(%s),  // exceptions count\n' % exception_var)
+    data_file.write('base::size(%s),  // exceptions count\n' % exception_var)
     data_file.write('%s,  // exceptions\n' % exception_var)
   else:
     data_file.write('0,  // exceptions count\n')
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
index b608b38..c24085a6 100644
--- a/gpu/config/software_rendering_list.json
+++ b/gpu/config/software_rendering_list.json
@@ -920,10 +920,9 @@
           "value": "3.19.1"
         }
       },
-      "vendor_id": "0x8086",
-      "device_id": ["0x0402", "0x0406", "0x040a", "0x040b", "0x040e",
-                    "0x0a02", "0x0a06", "0x0a0a", "0x0a0b", "0x0a0e",
-                    "0x0d02", "0x0d06", "0x0d0a", "0x0d0b", "0x0d0e"],
+      "gpu_series": [
+        "intel_haswell"
+      ],
       "features": [
         "all"
       ]
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc
index 97dd4e4..1a2ceaf 100644
--- a/gpu/ipc/service/direct_composition_child_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_child_surface_win.cc
@@ -71,22 +71,32 @@
   pbuffer_attribs.push_back(EGL_NONE);
   default_surface_ =
       eglCreatePbufferSurface(display, GetConfig(), &pbuffer_attribs[0]);
-  CHECK(!!default_surface_);
+  if (!default_surface_) {
+    DLOG(ERROR) << "eglCreatePbufferSurface failed with error "
+                << ui::GetLastEGLErrorString();
+    // It is likely that restoring the context will fail, so call Restore here
+    // to avoid the assert during ScopedReleaseCurrent destruction.
+    ignore_result(release_current.Restore());
+    return false;
+  }
 
-  return release_current.Restore();
-}
+  if (!release_current.Restore()) {
+    DLOG(ERROR) << "Failed to restore context with error "
+                << ui::GetLastEGLErrorString();
+    return false;
+  }
 
-void DirectCompositionChildSurfaceWin::ReleaseCurrentSurface() {
-  ReleaseDrawTexture(true);
-  dcomp_surface_.Reset();
-  swap_chain_.Reset();
+  return true;
 }
 
 bool DirectCompositionChildSurfaceWin::InitializeSurface() {
   TRACE_EVENT1("gpu", "DirectCompositionChildSurfaceWin::InitializeSurface()",
                "enable_dc_layers_", enable_dc_layers_);
-  DCHECK(!dcomp_surface_);
-  DCHECK(!swap_chain_);
+  if (!ReleaseDrawTexture(true /* will_discard */))
+    return false;
+  dcomp_surface_.Reset();
+  swap_chain_.Reset();
+
   DXGI_FORMAT output_format =
       is_hdr_ ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_B8G8R8A8_UNORM;
   if (enable_dc_layers_) {
@@ -96,16 +106,22 @@
         size_.width(), size_.height(), output_format,
         DXGI_ALPHA_MODE_PREMULTIPLIED, dcomp_surface_.GetAddressOf());
     has_been_rendered_to_ = false;
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "CreateSurface failed with error " << std::hex << hr;
+      return false;
+    }
   } else {
     DXGI_ALPHA_MODE alpha_mode =
         has_alpha_ ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;
     Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
     d3d11_device_.CopyTo(dxgi_device.GetAddressOf());
+    DCHECK(dxgi_device);
     Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
     dxgi_device->GetAdapter(dxgi_adapter.GetAddressOf());
+    DCHECK(dxgi_adapter);
     Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
     dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
+    DCHECK(dxgi_factory);
 
     DXGI_SWAP_CHAIN_DESC1 desc = {};
     desc.Width = size_.width();
@@ -123,47 +139,63 @@
         d3d11_device_.Get(), &desc, nullptr, swap_chain_.GetAddressOf());
     has_been_rendered_to_ = false;
     first_swap_ = true;
-    return SUCCEEDED(hr);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "CreateSwapChainForComposition failed with error "
+                  << std::hex << hr;
+      return false;
+    }
   }
   return true;
 }
 
-void DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
+bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
   DCHECK(!gl::GLContext::GetCurrent());
   if (real_surface_) {
     eglDestroySurface(GetDisplay(), real_surface_);
     real_surface_ = nullptr;
   }
+
+  if (dcomp_surface_.Get() == g_current_surface)
+    g_current_surface = nullptr;
+
   if (draw_texture_) {
     draw_texture_.Reset();
     if (dcomp_surface_) {
       HRESULT hr = dcomp_surface_->EndDraw();
-      CHECK(SUCCEEDED(hr));
+      if (FAILED(hr)) {
+        DLOG(ERROR) << "EndDraw failed with error " << std::hex << hr;
+        return false;
+      }
       dcomp_surface_serial_++;
     } else if (!will_discard) {
       DXGI_PRESENT_PARAMETERS params = {};
       RECT dirty_rect = swap_rect_.ToRECT();
       params.DirtyRectsCount = 1;
       params.pDirtyRects = &dirty_rect;
-      swap_chain_->Present1(vsync_enabled_ && !first_swap_ ? 1 : 0, 0, &params);
+      HRESULT hr = swap_chain_->Present1(vsync_enabled_ && !first_swap_ ? 1 : 0,
+                                         0, &params);
+      if (FAILED(hr)) {
+        DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
+        return false;
+      }
       if (first_swap_) {
         // Wait for the GPU to finish executing its commands before
         // committing the DirectComposition tree, or else the swapchain
         // may flicker black when it's first presented.
+        first_swap_ = false;
         Microsoft::WRL::ComPtr<IDXGIDevice2> dxgi_device2;
-        HRESULT hr = d3d11_device_.CopyTo(dxgi_device2.GetAddressOf());
-        DCHECK(SUCCEEDED(hr));
+        d3d11_device_.CopyTo(dxgi_device2.GetAddressOf());
+        DCHECK(dxgi_device2);
         base::WaitableEvent event(
             base::WaitableEvent::ResetPolicy::AUTOMATIC,
             base::WaitableEvent::InitialState::NOT_SIGNALED);
-        dxgi_device2->EnqueueSetEvent(event.handle());
+        hr = dxgi_device2->EnqueueSetEvent(event.handle());
+        DCHECK(SUCCEEDED(hr));
         event.Wait();
-        first_swap_ = false;
       }
     }
   }
-  if (dcomp_surface_.Get() == g_current_surface)
-    g_current_surface = nullptr;
+  return true;
 }
 
 void DirectCompositionChildSurfaceWin::Destroy() {
@@ -183,7 +215,8 @@
   }
   if (dcomp_surface_ && (dcomp_surface_.Get() == g_current_surface)) {
     HRESULT hr = dcomp_surface_->EndDraw();
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr))
+      DLOG(ERROR) << "EndDraw failed with error " << std::hex << hr;
     g_current_surface = nullptr;
   }
   draw_texture_.Reset();
@@ -207,7 +240,8 @@
   // PresentationCallback is handled by DirectCompositionSurfaceWin. The child
   // surface doesn't need provide presentation feedback.
   DCHECK(!callback);
-  ReleaseDrawTexture(false);
+  if (!ReleaseDrawTexture(false /* will_discard */))
+    return gfx::SwapResult::SWAP_FAILED;
   return gfx::SwapResult::SWAP_ACK;
 }
 
@@ -223,12 +257,18 @@
   if (g_current_surface != dcomp_surface_.Get()) {
     if (g_current_surface) {
       HRESULT hr = g_current_surface->SuspendDraw();
-      CHECK(SUCCEEDED(hr));
+      if (FAILED(hr)) {
+        DLOG(ERROR) << "SuspendDraw failed with error " << std::hex << hr;
+        return false;
+      }
       g_current_surface = nullptr;
     }
     if (draw_texture_) {
       HRESULT hr = dcomp_surface_->ResumeDraw();
-      CHECK(SUCCEEDED(hr));
+      if (FAILED(hr)) {
+        DLOG(ERROR) << "ResumeDraw failed with error " << std::hex << hr;
+        return false;
+      }
       g_current_surface = dcomp_surface_.Get();
     }
   }
@@ -257,7 +297,6 @@
 
   if ((enable_dc_layers_ && !dcomp_surface_) ||
       (!enable_dc_layers_ && !swap_chain_)) {
-    ReleaseCurrentSurface();
     if (!InitializeSurface()) {
       DLOG(ERROR) << "InitializeSurface failed";
       // It is likely that restoring the context will fail, so call Restore here
@@ -273,22 +312,25 @@
     return false;
   }
 
-  CHECK(!g_current_surface);
+  DCHECK(!g_current_surface);
 
-  RECT rect = rectangle.ToRECT();
+  swap_rect_ = rectangle;
+  draw_offset_ = gfx::Vector2d();
+
   if (dcomp_surface_) {
     POINT update_offset;
+    const RECT rect = rectangle.ToRECT();
     HRESULT hr = dcomp_surface_->BeginDraw(
         &rect, IID_PPV_ARGS(draw_texture_.GetAddressOf()), &update_offset);
-    draw_offset_ = gfx::Point(update_offset) - gfx::Rect(rect).origin();
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "BeginDraw failed with error " << std::hex << hr;
+      return false;
+    }
+    draw_offset_ = gfx::Point(update_offset) - rectangle.origin();
   } else {
-    HRESULT hr =
-        swap_chain_->GetBuffer(0, IID_PPV_ARGS(draw_texture_.GetAddressOf()));
-    swap_rect_ = rectangle;
-    draw_offset_ = gfx::Vector2d();
-    CHECK(SUCCEEDED(hr));
+    swap_chain_->GetBuffer(0, IID_PPV_ARGS(draw_texture_.GetAddressOf()));
   }
+  DCHECK(draw_texture_);
   has_been_rendered_to_ = true;
 
   g_current_surface = dcomp_surface_.Get();
@@ -307,9 +349,18 @@
   real_surface_ = eglCreatePbufferFromClientBuffer(
       GetDisplay(), EGL_D3D_TEXTURE_ANGLE, buffer, GetConfig(),
       &pbuffer_attribs[0]);
+  if (!real_surface_) {
+    DLOG(ERROR) << "eglCreatePbufferFromClientBuffer failed with error "
+                << ui::GetLastEGLErrorString();
+    // It is likely that restoring the context will fail, so call Restore here
+    // to avoid the assert during ScopedReleaseCurrent destruction.
+    ignore_result(release_current.Restore());
+    return false;
+  }
 
   if (!release_current.Restore()) {
-    DLOG(ERROR) << "Failed to restore context";
+    DLOG(ERROR) << "Failed to restore context with error "
+                << ui::GetLastEGLErrorString();
     return false;
   }
 
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.h b/gpu/ipc/service/direct_composition_child_surface_win.h
index b4ec2fb..c9b6ee1 100644
--- a/gpu/ipc/service/direct_composition_child_surface_win.h
+++ b/gpu/ipc/service/direct_composition_child_surface_win.h
@@ -53,12 +53,13 @@
   ~DirectCompositionChildSurfaceWin() override;
 
  private:
-  void ReleaseCurrentSurface();
+  // Releases previous surface or swap chain, and initializes new surface or
+  // swap chain.
   bool InitializeSurface();
   // Release the texture that's currently being drawn to. If will_discard is
   // true then the surface should be discarded without swapping any contents
-  // to it.
-  void ReleaseDrawTexture(bool will_discard);
+  // to it. Returns false if this fails.
+  bool ReleaseDrawTexture(bool will_discard);
 
   // This is a placeholder surface used when not rendering to the
   // DirectComposition surface.
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index 5f412ead..4d7d9d3 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -84,6 +84,26 @@
   DISALLOW_COPY_AND_ASSIGN(PresentationHistory);
 };
 
+class ScopedReleaseKeyedMutex {
+ public:
+  ScopedReleaseKeyedMutex(Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex,
+                          UINT64 key)
+      : keyed_mutex_(keyed_mutex), key_(key) {
+    DCHECK(keyed_mutex);
+  }
+
+  ~ScopedReleaseKeyedMutex() {
+    HRESULT hr = keyed_mutex_->ReleaseSync(key_);
+    DCHECK(SUCCEEDED(hr));
+  }
+
+ private:
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex_;
+  UINT64 key_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedReleaseKeyedMutex);
+};
+
 gfx::Size g_overlay_monitor_size;
 
 bool g_supports_scaled_overlays = true;
@@ -107,31 +127,37 @@
   Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
       gl::QueryD3D11DeviceObjectFromANGLE();
   if (!d3d11_device) {
-    DLOG(ERROR) << "Failing to create overlay swapchain because couldn't "
-                   "retrieve D3D11 device from ANGLE.";
+    DLOG(ERROR) << "Not using overlays because failed to retrieve D3D11 device "
+                   "from ANGLE";
     return false;
   }
 
+  // This can fail if the D3D device is "Microsoft Basic Display Adapter".
   Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
   if (FAILED(d3d11_device.CopyTo(video_device.GetAddressOf()))) {
-    DLOG(ERROR) << "Failing to create overlay swapchain because couldn't "
-                   "retrieve video device from D3D11 device.";
+    DLOG(ERROR) << "Not using overlays because failed to retrieve video device "
+                   "from D3D11 device";
     return false;
   }
+  DCHECK(video_device);
 
   Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
   d3d11_device.CopyTo(dxgi_device.GetAddressOf());
+  DCHECK(dxgi_device);
   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
   dxgi_device->GetAdapter(dxgi_adapter.GetAddressOf());
+  DCHECK(dxgi_adapter);
 
   unsigned int i = 0;
   while (true) {
     Microsoft::WRL::ComPtr<IDXGIOutput> output;
     if (FAILED(dxgi_adapter->EnumOutputs(i++, output.GetAddressOf())))
       break;
+    DCHECK(output);
     Microsoft::WRL::ComPtr<IDXGIOutput3> output3;
     if (FAILED(output.CopyTo(output3.GetAddressOf())))
       continue;
+    DCHECK(output3);
 
     UINT flags = 0;
     if (FAILED(output3->CheckOverlaySupport(DXGI_FORMAT_YUY2,
@@ -174,7 +200,7 @@
   bool Initialize(HWND window);
   bool CommitAndClearPendingOverlays();
   bool ScheduleDCLayer(const ui::DCRendererLayerParams& params);
-  void InitializeVideoProcessor(const gfx::Size& input_size,
+  bool InitializeVideoProcessor(const gfx::Size& input_size,
                                 const gfx::Size& output_size);
 
   const Microsoft::WRL::ComPtr<ID3D11VideoProcessor>& video_processor() const {
@@ -217,7 +243,8 @@
   // These functions return true if the visual tree was changed.
   bool InitVisual(size_t i);
   bool UpdateVisualForVideo(VisualInfo* visual_info,
-                            const ui::DCRendererLayerParams& params);
+                            const ui::DCRendererLayerParams& params,
+                            bool* present_failed);
   bool UpdateVisualForBackbuffer(VisualInfo* visual_info,
                                  const ui::DCRendererLayerParams& params);
   bool UpdateVisualClip(VisualInfo* visual_info,
@@ -255,11 +282,12 @@
 class DCLayerTree::SwapChainPresenter {
  public:
   SwapChainPresenter(DCLayerTree* surface,
-                     Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
-
+                     Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
+                     Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
+                     Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context);
   ~SwapChainPresenter();
 
-  void PresentToSwapChain(const ui::DCRendererLayerParams& overlay,
+  bool PresentToSwapChain(const ui::DCRendererLayerParams& overlay,
                           const gfx::Size& video_input_size,
                           const gfx::Size& swap_chain_size);
 
@@ -276,7 +304,7 @@
   // Returns true if the video processor changed.
   bool InitializeVideoProcessor(const gfx::Size& in_size,
                                 const gfx::Size& out_size);
-  void ReallocateSwapChain(bool yuy2);
+  bool ReallocateSwapChain(bool yuy2);
   bool ShouldBeYUY2();
 
   DCLayerTree* surface_;
@@ -312,37 +340,42 @@
 };
 
 bool DCLayerTree::Initialize(HWND window) {
-  HRESULT hr = d3d11_device_.CopyTo(video_device_.GetAddressOf());
-  if (FAILED(hr))
+  // This can fail if the D3D device is "Microsoft Basic Display Adapter".
+  if (FAILED(d3d11_device_.CopyTo(video_device_.GetAddressOf()))) {
+    DLOG(ERROR) << "Failed to retrieve video device from D3D11 device";
     return false;
+  }
+  DCHECK(video_device_);
 
   Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
   d3d11_device_->GetImmediateContext(context.GetAddressOf());
-  hr = context.CopyTo(video_context_.GetAddressOf());
-  if (FAILED(hr))
-    return false;
+  DCHECK(context);
+  context.CopyTo(video_context_.GetAddressOf());
+  DCHECK(video_context_);
 
   Microsoft::WRL::ComPtr<IDCompositionDesktopDevice> desktop_device;
   dcomp_device_.CopyTo(desktop_device.GetAddressOf());
+  DCHECK(desktop_device);
 
-  hr = desktop_device->CreateTargetForHwnd(window, TRUE,
-                                           dcomp_target_.GetAddressOf());
-  if (FAILED(hr))
+  HRESULT hr = desktop_device->CreateTargetForHwnd(
+      window, TRUE, dcomp_target_.GetAddressOf());
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "CreateTargetForHwnd failed with error " << std::hex << hr;
     return false;
+  }
 
-  hr = dcomp_device_->CreateVisual(root_visual_.GetAddressOf());
-  if (FAILED(hr))
-    return false;
-
+  dcomp_device_->CreateVisual(root_visual_.GetAddressOf());
+  DCHECK(root_visual_);
   dcomp_target_->SetRoot(root_visual_.Get());
+
   return true;
 }
 
-void DCLayerTree::InitializeVideoProcessor(const gfx::Size& input_size,
+bool DCLayerTree::InitializeVideoProcessor(const gfx::Size& input_size,
                                            const gfx::Size& output_size) {
-  if (SizeContains(video_input_size_, input_size) &&
+  if (video_processor_ && SizeContains(video_input_size_, input_size) &&
       SizeContains(video_output_size_, output_size))
-    return;
+    return true;
   video_input_size_ = input_size;
   video_output_size_ = output_size;
 
@@ -361,15 +394,23 @@
   desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
   HRESULT hr = video_device_->CreateVideoProcessorEnumerator(
       &desc, video_processor_enumerator_.GetAddressOf());
-  CHECK(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "CreateVideoProcessorEnumerator failed with error "
+                << std::hex << hr;
+    return false;
+  }
 
   hr = video_device_->CreateVideoProcessor(video_processor_enumerator_.Get(), 0,
                                            video_processor_.GetAddressOf());
-  CHECK(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "CreateVideoProcessor failed with error " << std::hex << hr;
+    return false;
+  }
 
   // Auto stream processing (the default) can hurt power consumption.
   video_context_->VideoProcessorSetStreamAutoProcessingMode(
       video_processor_.Get(), 0, FALSE);
+  return true;
 }
 
 Microsoft::WRL::ComPtr<IDXGISwapChain1>
@@ -381,19 +422,18 @@
 
 DCLayerTree::SwapChainPresenter::SwapChainPresenter(
     DCLayerTree* surface,
-    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device)
-    : surface_(surface), d3d11_device_(d3d11_device) {
-  HRESULT hr = d3d11_device_.CopyTo(video_device_.GetAddressOf());
-  CHECK(SUCCEEDED(hr));
-  Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
-  d3d11_device_->GetImmediateContext(context.GetAddressOf());
-  hr = context.CopyTo(video_context_.GetAddressOf());
-  CHECK(SUCCEEDED(hr));
+    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
+    Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
+    Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context)
+    : surface_(surface),
+      d3d11_device_(d3d11_device),
+      video_device_(video_device),
+      video_context_(video_context) {
   HMODULE dcomp = ::GetModuleHandleA("dcomp.dll");
   CHECK(dcomp);
   create_surface_handle_function_ =
       reinterpret_cast<PFN_DCOMPOSITION_CREATE_SURFACE_HANDLE>(
-          GetProcAddress(dcomp, "DCompositionCreateSurfaceHandle"));
+          ::GetProcAddress(dcomp, "DCompositionCreateSurfaceHandle"));
   CHECK(create_surface_handle_function_);
 }
 
@@ -427,7 +467,7 @@
       uv_image_size.width() != texture_size.width() / 2 ||
       y_image_memory->format() != gfx::BufferFormat::R_8 ||
       uv_image_memory->format() != gfx::BufferFormat::RG_88) {
-    DVLOG(ERROR) << "Invalid NV12 GLImageMemory properties.";
+    DLOG(ERROR) << "Invalid NV12 GLImageMemory properties.";
     return false;
   }
 
@@ -451,17 +491,25 @@
     Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
     HRESULT hr = d3d11_device_->CreateTexture2D(
         &desc, nullptr, staging_texture_.GetAddressOf());
-    CHECK(SUCCEEDED(hr)) << "Creating D3D11 video upload texture failed: "
-                         << std::hex << hr;
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Creating D3D11 video upload texture failed: " << std::hex
+                  << hr;
+      return false;
+    }
+    DCHECK(staging_texture_);
     staging_texture_size_ = texture_size;
   }
   Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
   d3d11_device_->GetImmediateContext(context.GetAddressOf());
+  DCHECK(context);
   D3D11_MAPPED_SUBRESOURCE mapped_resource;
   HRESULT hr = context->Map(staging_texture_.Get(), 0, D3D11_MAP_WRITE_DISCARD,
                             0, &mapped_resource);
-  CHECK(SUCCEEDED(hr)) << "Mapping D3D11 video upload texture failed: "
-                       << std::hex << hr;
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "Mapping D3D11 video upload texture failed: " << std::hex
+                << hr;
+    return false;
+  }
 
   size_t dest_stride = mapped_resource.RowPitch;
   for (int y = 0; y < texture_size.height(); y++) {
@@ -485,7 +533,7 @@
   return true;
 }
 
-void DCLayerTree::SwapChainPresenter::PresentToSwapChain(
+bool DCLayerTree::SwapChainPresenter::PresentToSwapChain(
     const ui::DCRendererLayerParams& params,
     const gfx::Size& video_input_size,
     const gfx::Size& swap_chain_size) {
@@ -501,10 +549,11 @@
   if (!image_dxgi && (!y_image_memory || !uv_image_memory)) {
     DLOG(ERROR) << "Video GLImages are missing";
     last_gl_images_.clear();
-    return;
+    return false;
   }
 
-  InitializeVideoProcessor(video_input_size, swap_chain_size);
+  if (!InitializeVideoProcessor(video_input_size, swap_chain_size))
+    return false;
 
   bool yuy2_swapchain = ShouldBeYUY2();
   bool first_present = false;
@@ -513,13 +562,12 @@
        !failed_to_create_yuy2_swapchain_)) {
     first_present = true;
     swap_chain_size_ = swap_chain_size;
-    swap_chain_.Reset();
     ReallocateSwapChain(yuy2_swapchain);
   } else if (last_gl_images_ == params.image) {
     // The swap chain is presenting the same images as last swap, which means
     // that the images were never returned to the video decoder and should
     // have the same contents as last time. It shouldn't need to be redrawn.
-    return;
+    return true;
   }
 
   last_gl_images_ = params.image;
@@ -530,8 +578,10 @@
   if (image_dxgi) {
     input_texture = image_dxgi->texture();
     input_level = (UINT)image_dxgi->level();
-    if (!input_texture)
-      return;
+    if (!input_texture) {
+      DLOG(ERROR) << "Video image has no texture";
+      return false;
+    }
     // Keyed mutex may not exist.
     keyed_mutex = image_dxgi->keyed_mutex();
     staging_texture_.Reset();
@@ -539,7 +589,7 @@
     DCHECK(y_image_memory);
     DCHECK(uv_image_memory);
     if (!UploadVideoImages(y_image_memory, uv_image_memory))
-      return;
+      return false;
     DCHECK(staging_texture_);
     input_texture = staging_texture_;
     input_level = 0;
@@ -554,7 +604,11 @@
     HRESULT hr = video_device_->CreateVideoProcessorOutputView(
         texture.Get(), video_processor_enumerator_.Get(), &out_desc,
         out_view_.GetAddressOf());
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "CreateVideoProcessorOutputView failed with error "
+                  << std::hex << hr;
+      return false;
+    }
   }
 
   // TODO(jbauman): Use correct colorspace.
@@ -564,6 +618,7 @@
   }
   Microsoft::WRL::ComPtr<ID3D11VideoContext1> context1;
   if (SUCCEEDED(video_context_.CopyTo(context1.GetAddressOf()))) {
+    DCHECK(context1);
     context1->VideoProcessorSetStreamColorSpace1(
         video_processor_.Get(), 0,
         gfx::ColorSpaceWin::GetDXGIColorSpace(src_color_space));
@@ -585,6 +640,7 @@
 
   Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3;
   if (SUCCEEDED(swap_chain_.CopyTo(swap_chain3.GetAddressOf()))) {
+    DCHECK(swap_chain3);
     DXGI_COLOR_SPACE_TYPE color_space =
         gfx::ColorSpaceWin::GetDXGIColorSpace(output_color_space);
     if (is_yuy2_swapchain_) {
@@ -632,6 +688,7 @@
   }
 
   {
+    base::Optional<ScopedReleaseKeyedMutex> release_keyed_mutex;
     if (keyed_mutex) {
       // The producer may still be using this texture for a short period of
       // time, so wait long enough to hopefully avoid glitches. For example,
@@ -642,9 +699,11 @@
       HRESULT hr = keyed_mutex->AcquireSync(0, kMaxSyncTimeMs);
       if (FAILED(hr)) {
         DLOG(ERROR) << "Error acquiring keyed mutex: " << std::hex << hr;
-        return;
+        return false;
       }
+      release_keyed_mutex.emplace(keyed_mutex, 0);
     }
+
     D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC in_desc = {};
     in_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
     in_desc.Texture2D.ArraySlice = input_level;
@@ -652,7 +711,11 @@
     HRESULT hr = video_device_->CreateVideoProcessorInputView(
         input_texture.Get(), video_processor_enumerator_.Get(), &in_desc,
         in_view.GetAddressOf());
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "CreateVideoProcessorInputView failed with error "
+                  << std::hex << hr;
+      return false;
+    }
 
     D3D11_VIDEO_PROCESSOR_STREAM stream = {};
     stream.Enable = true;
@@ -672,15 +735,18 @@
 
     hr = video_context_->VideoProcessorBlt(video_processor_.Get(),
                                            out_view_.Get(), 0, 1, &stream);
-    CHECK(SUCCEEDED(hr));
-    if (keyed_mutex) {
-      HRESULT hr = keyed_mutex->ReleaseSync(0);
-      DCHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "VideoProcessorBlt failed with error " << std::hex << hr;
+      return false;
     }
   }
 
   if (first_present) {
-    swap_chain_->Present(0, 0);
+    HRESULT hr = swap_chain_->Present(0, 0);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Present failed with error " << std::hex << hr;
+      return false;
+    }
 
     // DirectComposition can display black for a swapchain between the first
     // and second time it's presented to - maybe the first Present can get
@@ -689,29 +755,34 @@
     // first Present() after this needs to have SyncInterval > 0, or else the
     // workaround doesn't help.
     Microsoft::WRL::ComPtr<ID3D11Texture2D> dest_texture;
-    HRESULT hr =
-        swap_chain_->GetBuffer(0, IID_PPV_ARGS(dest_texture.GetAddressOf()));
-    DCHECK(SUCCEEDED(hr));
+    swap_chain_->GetBuffer(0, IID_PPV_ARGS(dest_texture.GetAddressOf()));
+    DCHECK(dest_texture);
     Microsoft::WRL::ComPtr<ID3D11Texture2D> src_texture;
     hr = swap_chain_->GetBuffer(1, IID_PPV_ARGS(src_texture.GetAddressOf()));
-    DCHECK(SUCCEEDED(hr));
+    DCHECK(src_texture);
     Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
     d3d11_device_->GetImmediateContext(context.GetAddressOf());
+    DCHECK(context);
     context->CopyResource(dest_texture.Get(), src_texture.Get());
 
     // Additionally wait for the GPU to finish executing its commands, or
     // there still may be a black flicker when presenting expensive content
     // (e.g. 4k video).
     Microsoft::WRL::ComPtr<IDXGIDevice2> dxgi_device2;
-    hr = d3d11_device_.CopyTo(dxgi_device2.GetAddressOf());
-    DCHECK(SUCCEEDED(hr));
+    d3d11_device_.CopyTo(dxgi_device2.GetAddressOf());
+    DCHECK(dxgi_device2);
     base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                               base::WaitableEvent::InitialState::NOT_SIGNALED);
-    dxgi_device2->EnqueueSetEvent(event.handle());
+    hr = dxgi_device2->EnqueueSetEvent(event.handle());
+    DCHECK(SUCCEEDED(hr));
     event.Wait();
   }
 
-  swap_chain_->Present(1, 0);
+  HRESULT hr = swap_chain_->Present(1, 0);
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "Present failed with error " << std::hex << hr;
+    return false;
+  }
 
   UMA_HISTOGRAM_BOOLEAN("GPU.DirectComposition.SwapchainFormat",
                         is_yuy2_swapchain_);
@@ -719,6 +790,7 @@
 
   Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
   if (SUCCEEDED(swap_chain_.CopyTo(swap_chain_media.GetAddressOf()))) {
+    DCHECK(swap_chain_media);
     DXGI_FRAME_STATISTICS_MEDIA stats = {};
     if (SUCCEEDED(swap_chain_media->GetFrameStatisticsMedia(&stats))) {
       base::UmaHistogramSparse("GPU.DirectComposition.CompositionMode",
@@ -726,6 +798,7 @@
       presentation_history_.AddSample(stats.CompositionMode);
     }
   }
+  return true;
 }
 
 bool DCLayerTree::SwapChainPresenter::InitializeVideoProcessor(
@@ -733,10 +806,12 @@
     const gfx::Size& out_size) {
   if (video_processor_ && SizeContains(processor_input_size_, in_size) &&
       SizeContains(processor_output_size_, out_size))
-    return false;
+    return true;
   processor_input_size_ = in_size;
   processor_output_size_ = out_size;
-  surface_->InitializeVideoProcessor(in_size, out_size);
+
+  if (!surface_->InitializeVideoProcessor(in_size, out_size))
+    return false;
 
   video_processor_enumerator_ = surface_->video_processor_enumerator();
   video_processor_ = surface_->video_processor();
@@ -746,19 +821,21 @@
   return true;
 }
 
-void DCLayerTree::SwapChainPresenter::ReallocateSwapChain(bool yuy2) {
+bool DCLayerTree::SwapChainPresenter::ReallocateSwapChain(bool yuy2) {
   TRACE_EVENT0("gpu", "DCLayerTree::SwapChainPresenter::ReallocateSwapChain");
-  DCHECK(!swap_chain_);
+  swap_chain_.Reset();
+  out_view_.Reset();
 
   Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
   d3d11_device_.CopyTo(dxgi_device.GetAddressOf());
+  DCHECK(dxgi_device);
   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
   dxgi_device->GetAdapter(dxgi_adapter.GetAddressOf());
-  Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
-  dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
-
+  DCHECK(dxgi_adapter);
   Microsoft::WRL::ComPtr<IDXGIFactoryMedia> media_factory;
-  dxgi_factory.CopyTo(media_factory.GetAddressOf());
+  dxgi_adapter->GetParent(IID_PPV_ARGS(media_factory.GetAddressOf()));
+  DCHECK(media_factory);
+
   DXGI_SWAP_CHAIN_DESC1 desc = {};
   DCHECK(!swap_chain_size_.IsEmpty());
   desc.Width = swap_chain_size_.width();
@@ -777,6 +854,11 @@
   HANDLE handle;
   HRESULT hr = create_surface_handle_function_(COMPOSITIONOBJECT_ALL_ACCESS,
                                                nullptr, &handle);
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "DCompositionCreateSurfaceHandle failed with error "
+                << std::hex << hr;
+    return false;
+  }
   swap_chain_handle_.Set(handle);
 
   if (is_yuy2_swapchain_ != yuy2) {
@@ -796,21 +878,25 @@
         swap_chain_.GetAddressOf());
     is_yuy2_swapchain_ = SUCCEEDED(hr);
     failed_to_create_yuy2_swapchain_ = !is_yuy2_swapchain_;
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Failed to create YUY2 swap chain with error " << std::hex
+                  << hr << ". Falling back to BGRA";
+    }
   }
 
   if (!is_yuy2_swapchain_) {
-    if (yuy2) {
-      DLOG(ERROR) << "YUY2 creation failed with " << std::hex << hr
-                  << ". Falling back to BGRA";
-    }
     desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
     desc.Flags = 0;
     hr = media_factory->CreateSwapChainForCompositionSurfaceHandle(
         d3d11_device_.Get(), swap_chain_handle_.Get(), &desc, nullptr,
         swap_chain_.GetAddressOf());
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Failed to create BGRA swap chain with error " << std::hex
+                  << hr;
+      return false;
+    }
   }
-  out_view_.Reset();
+  return true;
 }
 
 bool DCLayerTree::InitVisual(size_t i) {
@@ -821,19 +907,21 @@
   DCHECK(!visual_info->clip_visual);
   Microsoft::WRL::ComPtr<IDCompositionVisual2> visual;
   dcomp_device_->CreateVisual(visual_info->clip_visual.GetAddressOf());
+  DCHECK(visual_info->clip_visual);
   dcomp_device_->CreateVisual(visual.GetAddressOf());
+  DCHECK(visual);
   visual_info->content_visual = visual;
   visual_info->clip_visual->AddVisual(visual.Get(), FALSE, nullptr);
-
   IDCompositionVisual2* last_visual =
       (i > 0) ? visual_info_[i - 1].clip_visual.Get() : nullptr;
   root_visual_->AddVisual(visual_info->clip_visual.Get(), TRUE, last_visual);
   return true;
 }
 
-bool DCLayerTree::UpdateVisualForVideo(
-    VisualInfo* visual_info,
-    const ui::DCRendererLayerParams& params) {
+bool DCLayerTree::UpdateVisualForVideo(VisualInfo* visual_info,
+                                       const ui::DCRendererLayerParams& params,
+                                       bool* present_failed) {
+  *present_failed = false;
   bool changed = false;
   // This visual's content was a DC surface, but now it'll be a swap chain.
   if (visual_info->surface) {
@@ -855,21 +943,24 @@
   if (swap_chain_size.IsEmpty()) {
     // This visual's content was a swap chain, but now it'll be empty.
     if (visual_info->swap_chain) {
-      changed = true;
       visual_info->swap_chain.Reset();
       visual_info->swap_chain_presenter.reset();
       dc_visual->SetContent(nullptr);
+      changed = true;
     }
     return changed;
   }
 
   if (!visual_info->swap_chain_presenter) {
-    visual_info->swap_chain_presenter =
-        std::make_unique<SwapChainPresenter>(this, d3d11_device_);
+    visual_info->swap_chain_presenter = std::make_unique<SwapChainPresenter>(
+        this, d3d11_device_, video_device_, video_context_);
   }
 
-  visual_info->swap_chain_presenter->PresentToSwapChain(
-      params, video_input_size, swap_chain_size);
+  if (!visual_info->swap_chain_presenter->PresentToSwapChain(
+          params, video_input_size, swap_chain_size)) {
+    *present_failed = true;
+    return changed;
+  }
 
   // This visual's content was a different swap chain.
   if (visual_info->swap_chain !=
@@ -906,6 +997,7 @@
     dc_visual->SetOffsetY(bounds_rect.y());
     Microsoft::WRL::ComPtr<IDCompositionMatrixTransform> dcomp_transform;
     dcomp_device_->CreateMatrixTransform(dcomp_transform.GetAddressOf());
+    DCHECK(dcomp_transform);
     D2D_MATRIX_3X2_F d2d_matrix = {{{final_transform.matrix().get(0, 0),
                                      final_transform.matrix().get(0, 1),
                                      final_transform.matrix().get(1, 0),
@@ -1025,6 +1117,7 @@
     if (params.is_clipped) {
       Microsoft::WRL::ComPtr<IDCompositionRectangleClip> clip;
       dcomp_device_->CreateRectangleClip(clip.GetAddressOf());
+      DCHECK(clip);
       gfx::Rect offset_clip = params.clip_rect;
       clip->SetLeft(offset_clip.x());
       clip->SetRight(offset_clip.right());
@@ -1079,19 +1172,23 @@
     VisualInfo* visual_info = &visual_info_[i];
 
     changed |= InitVisual(i);
-    if (params.image.size() >= 1 && params.image[0]) {
-      changed |= UpdateVisualForVideo(visual_info, params);
-    } else if (params.image.empty()) {
+    if (params.image.empty()) {
       changed |= UpdateVisualForBackbuffer(visual_info, params);
     } else {
-      CHECK(false);
+      bool present_failed = false;
+      changed |= UpdateVisualForVideo(visual_info, params, &present_failed);
+      if (present_failed)
+        return false;
     }
     changed |= UpdateVisualClip(visual_info, params);
   }
 
   if (changed) {
     HRESULT hr = dcomp_device_->Commit();
-    CHECK(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Commit failed with error " << std::hex << hr;
+      return false;
+    }
   }
 
   pending_overlays_.clear();
@@ -1241,8 +1338,8 @@
   default_surface_ =
       eglCreatePbufferSurface(display, GetConfig(), &pbuffer_attribs[0]);
   if (!default_surface_) {
-    LOG(ERROR) << "eglCreatePbufferSurface failed with error "
-               << ui::GetLastEGLErrorString();
+    DLOG(ERROR) << "eglCreatePbufferSurface failed with error "
+                << ui::GetLastEGLErrorString();
     return false;
   }
 
@@ -1304,11 +1401,27 @@
   gl::GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
       presentation_helper_.get(), callback);
   ui::ScopedReleaseCurrent release_current;
-  root_surface_->SwapBuffers(PresentationCallback());
-  layer_tree_->CommitAndClearPendingOverlays();
+
   child_window_.ClearInvalidContents();
+
+  bool failed = false;
+
+  if (root_surface_->SwapBuffers(PresentationCallback()) ==
+      gfx::SwapResult::SWAP_FAILED)
+    failed = true;
+
+  if (!layer_tree_->CommitAndClearPendingOverlays())
+    failed = true;
+
   if (!release_current.Restore())
+    failed = true;
+
+  if (failed) {
     scoped_swap_buffers.set_result(gfx::SwapResult::SWAP_FAILED);
+    base::UmaHistogramSparse("GPU.DirectComposition.SwapBuffersLastError",
+                             ::GetLastError());
+  }
+
   return scoped_swap_buffers.result();
 }
 
diff --git a/headless/lib/browser/protocol/target_handler.cc b/headless/lib/browser/protocol/target_handler.cc
index f4e5537..f6e5f9a 100644
--- a/headless/lib/browser/protocol/target_handler.cc
+++ b/headless/lib/browser/protocol/target_handler.cc
@@ -82,16 +82,32 @@
   return Response::OK();
 }
 
-Response TargetHandler::DisposeBrowserContext(const std::string& context_id,
-                                              bool* out_success) {
+Response TargetHandler::DisposeBrowserContext(const std::string& context_id) {
   HeadlessBrowserContext* context =
       browser()->GetBrowserContextForId(context_id);
 
-  *out_success = false;
-  if (context && context != browser()->GetDefaultBrowserContext() &&
-      context->GetAllWebContents().empty()) {
-    *out_success = true;
-    context->Close();
+  if (!context)
+    return Response::InvalidParams("browserContextId");
+
+  std::vector<HeadlessWebContents*> web_contents = context->GetAllWebContents();
+  while (!web_contents.empty()) {
+    for (auto* wc : web_contents)
+      wc->Close();
+    // Since HeadlessWebContents::Close spawns a nested run loop to await
+    // closing, new web_contents could be opened. We need to re-query pages and
+    // close them too.
+    web_contents = context->GetAllWebContents();
+  }
+  context->Close();
+  return Response::OK();
+}
+
+Response TargetHandler::GetBrowserContexts(
+    std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids) {
+  *browser_context_ids = std::make_unique<protocol::Array<protocol::String>>();
+  for (auto* context : browser()->GetAllBrowserContexts()) {
+    if (context != browser()->GetDefaultBrowserContext())
+      (*browser_context_ids)->addItem(context->Id());
   }
   return Response::OK();
 }
diff --git a/headless/lib/browser/protocol/target_handler.h b/headless/lib/browser/protocol/target_handler.h
index 0f5a182..fa57d8b 100644
--- a/headless/lib/browser/protocol/target_handler.h
+++ b/headless/lib/browser/protocol/target_handler.h
@@ -28,8 +28,10 @@
   Response CloseTarget(const std::string& target_id,
                        bool* out_success) override;
   Response CreateBrowserContext(std::string* out_context_id) override;
-  Response DisposeBrowserContext(const std::string& context_id,
-                                 bool* out_success) override;
+  Response DisposeBrowserContext(const std::string& context_id) override;
+  Response GetBrowserContexts(
+      std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids)
+      override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TargetHandler);
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc
index 6a3beb52..32a3321 100644
--- a/headless/lib/headless_devtools_client_browsertest.cc
+++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -513,7 +513,17 @@
 
   void OnDisposeBrowserContextResult(
       std::unique_ptr<target::DisposeBrowserContextResult> result) {
-    EXPECT_TRUE(result->GetSuccess());
+    devtools_client_->GetTarget()->GetExperimental()->GetBrowserContexts(
+        target::GetBrowserContextsParams::Builder().Build(),
+        base::BindOnce(&TargetDomainCreateAndDeleteBrowserContextTest::
+                           OnGetBrowserContexts,
+                       base::Unretained(this)));
+  }
+
+  void OnGetBrowserContexts(
+      std::unique_ptr<target::GetBrowserContextsResult> result) {
+    const std::vector<std::string>* contexts = result->GetBrowserContextIds();
+    EXPECT_EQ(0u, contexts->size());
     FinishAsynchronousTest();
   }
 
@@ -523,16 +533,27 @@
 
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeleteBrowserContextTest);
 
-class TargetDomainDisposeContextFailsIfInUse
-    : public HeadlessAsyncDevTooledBrowserTest {
+class TargetDomainDisposeContextSucceedsIfInUse
+    : public target::Observer,
+      public HeadlessAsyncDevTooledBrowserTest {
   void RunDevTooledTest() override {
     EXPECT_TRUE(embedded_test_server()->Start());
-
     EXPECT_EQ(1u, GetAllWebContents(browser()).size());
+
+    devtools_client_->GetTarget()->AddObserver(this);
+    devtools_client_->GetTarget()->SetDiscoverTargets(
+        target::SetDiscoverTargetsParams::Builder().SetDiscover(true).Build(),
+        base::BindOnce(&TargetDomainDisposeContextSucceedsIfInUse::
+                           OnDiscoverTargetsEnabled,
+                       base::Unretained(this)));
+  }
+
+  void OnDiscoverTargetsEnabled(
+      std::unique_ptr<target::SetDiscoverTargetsResult> result) {
     devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
         target::CreateBrowserContextParams::Builder().Build(),
         base::BindOnce(
-            &TargetDomainDisposeContextFailsIfInUse::OnContextCreated,
+            &TargetDomainDisposeContextSucceedsIfInUse::OnContextCreated,
             base::Unretained(this)));
   }
 
@@ -546,7 +567,7 @@
             .SetBrowserContextId(context_id_)
             .Build(),
         base::BindOnce(
-            &TargetDomainDisposeContextFailsIfInUse::OnCreateTargetResult,
+            &TargetDomainDisposeContextSucceedsIfInUse::OnCreateTargetResult,
             base::Unretained(this)));
   }
 
@@ -554,54 +575,38 @@
       std::unique_ptr<target::CreateTargetResult> result) {
     page_id_ = result->GetTargetId();
 
+    destroyed_targets_.clear();
     devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
         target::DisposeBrowserContextParams::Builder()
             .SetBrowserContextId(context_id_)
             .Build(),
-        base::BindOnce(&TargetDomainDisposeContextFailsIfInUse::
+        base::BindOnce(&TargetDomainDisposeContextSucceedsIfInUse::
                            OnDisposeBrowserContextResult,
                        base::Unretained(this)));
   }
 
+  void OnTargetDestroyed(const target::TargetDestroyedParams& params) override {
+    destroyed_targets_.push_back(params.GetTargetId());
+  }
+
   void OnDisposeBrowserContextResult(
       std::unique_ptr<target::DisposeBrowserContextResult> result) {
-    EXPECT_FALSE(result->GetSuccess());
-
-    // Close the page and try again.
-    devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
-        target::CloseTargetParams::Builder().SetTargetId(page_id_).Build(),
-        base::BindOnce(
-            &TargetDomainDisposeContextFailsIfInUse::OnCloseTargetResult,
-            base::Unretained(this)));
-  }
-
-  void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) {
-    EXPECT_TRUE(result->GetSuccess());
-
-    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
-        target::DisposeBrowserContextParams::Builder()
-            .SetBrowserContextId(context_id_)
-            .Build(),
-        base::BindOnce(&TargetDomainDisposeContextFailsIfInUse::
-                           OnDisposeBrowserContextResult2,
-                       base::Unretained(this)));
-  }
-
-  void OnDisposeBrowserContextResult2(
-      std::unique_ptr<target::DisposeBrowserContextResult> result) {
-    EXPECT_TRUE(result->GetSuccess());
+    EXPECT_EQ(destroyed_targets_.size(), 1u);
+    EXPECT_EQ(destroyed_targets_[0], page_id_);
+    devtools_client_->GetTarget()->RemoveObserver(this);
     FinishAsynchronousTest();
   }
 
  private:
+  std::vector<std::string> destroyed_targets_;
   std::string context_id_;
   std::string page_id_;
 };
 
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainDisposeContextFailsIfInUse);
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainDisposeContextSucceedsIfInUse);
 
 class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
-                                      public target::ExperimentalObserver,
+                                      public target::Observer,
                                       public page::Observer {
  public:
   void RunDevTooledTest() override {
@@ -612,7 +617,15 @@
     devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
     run_loop.Run();
 
-    devtools_client_->GetTarget()->GetExperimental()->AddObserver(this);
+    devtools_client_->GetTarget()->AddObserver(this);
+    devtools_client_->GetTarget()->SetDiscoverTargets(
+        target::SetDiscoverTargetsParams::Builder().SetDiscover(true).Build(),
+        base::BindOnce(&TargetDomainCreateTwoContexts::OnDiscoverTargetsEnabled,
+                       base::Unretained(this)));
+  }
+
+  void OnDiscoverTargetsEnabled(
+      std::unique_ptr<target::SetDiscoverTargetsResult> result) {
     devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
         target::CreateBrowserContextParams::Builder().Build(),
         base::BindOnce(&TargetDomainCreateTwoContexts::OnContextOneCreated,
@@ -640,6 +653,21 @@
     if (context_id_one_.empty() || context_id_two_.empty())
       return;
 
+    devtools_client_->GetTarget()->GetExperimental()->GetBrowserContexts(
+        target::GetBrowserContextsParams::Builder().Build(),
+        base::BindOnce(&TargetDomainCreateTwoContexts::OnGetBrowserContexts,
+                       base::Unretained(this)));
+  }
+
+  void OnGetBrowserContexts(
+      std::unique_ptr<target::GetBrowserContextsResult> result) {
+    const std::vector<std::string>* contexts = result->GetBrowserContextIds();
+    EXPECT_EQ(2u, contexts->size());
+    EXPECT_TRUE(std::find(contexts->begin(), contexts->end(),
+                          context_id_one_) != contexts->end());
+    EXPECT_TRUE(std::find(contexts->begin(), contexts->end(),
+                          context_id_two_) != contexts->end());
+
     devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
         target::CreateTargetParams::Builder()
             .SetUrl("about://blank")
@@ -809,52 +837,34 @@
         EXPECT_EQ("", value_value->GetString())
             << "Page 2 should not share cookies from page one";
 
-        devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
-            target::CloseTargetParams::Builder()
-                .SetTargetId(page_id_one_)
+        devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
+            target::DisposeBrowserContextParams::Builder()
+                .SetBrowserContextId(context_id_one_)
                 .Build(),
-            base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseTarget,
+            base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseContext,
                            base::Unretained(this)));
 
-        devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
-            target::CloseTargetParams::Builder()
-                .SetTargetId(page_id_two_)
+        devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
+            target::DisposeBrowserContextParams::Builder()
+                .SetBrowserContextId(context_id_two_)
                 .Build(),
-            base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseTarget,
+            base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseContext,
                            base::Unretained(this)));
-
-        devtools_client_->GetTarget()->GetExperimental()->RemoveObserver(this);
       }
     }
   }
 
-  void OnCloseTarget(std::unique_ptr<target::CloseTargetResult> result) {
-    page_close_count_++;
-
-    if (page_close_count_ < 2)
-      return;
-
-    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
-        target::DisposeBrowserContextParams::Builder()
-            .SetBrowserContextId(context_id_one_)
-            .Build(),
-        base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseContext,
-                       base::Unretained(this)));
-
-    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
-        target::DisposeBrowserContextParams::Builder()
-            .SetBrowserContextId(context_id_two_)
-            .Build(),
-        base::BindOnce(&TargetDomainCreateTwoContexts::OnCloseContext,
-                       base::Unretained(this)));
+  void OnTargetDestroyed(const target::TargetDestroyedParams& params) override {
+    ++page_close_count_;
   }
 
   void OnCloseContext(
       std::unique_ptr<target::DisposeBrowserContextResult> result) {
-    EXPECT_TRUE(result->GetSuccess());
     if (++context_closed_count_ < 2)
       return;
+    EXPECT_EQ(page_close_count_, 2);
 
+    devtools_client_->GetTarget()->RemoveObserver(this);
     FinishAsynchronousTest();
   }
 
diff --git a/headless/protocol_config.json b/headless/protocol_config.json
index 8f2f5bf2..f441c0c 100644
--- a/headless/protocol_config.json
+++ b/headless/protocol_config.json
@@ -12,7 +12,7 @@
         "options": [
             {
                 "domain": "Target",
-                "include": ["createTarget", "closeTarget", "createBrowserContext", "disposeBrowserContext"],
+                "include": ["createTarget", "closeTarget", "getBrowserContexts", "createBrowserContext", "disposeBrowserContext"],
                 "include_events": []
             },
             {
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index cd46c68..f052ca9 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -129,7 +129,7 @@
       }
       builders {
         name: "win10_chromium_x64_rel_ng"
-        equivalent_to { bucket: "luci.chromium.try" percentage: 10 }
+        equivalent_to { bucket: "luci.chromium.try" percentage: 100 }
       }
       builders {
         name: "win7_chromium_rel_loc_exp"
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 926238d..6f9af9e 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -556,6 +556,12 @@
     }
 
     builders {
+      name: "android-kitkat-arm-rel"
+      mixins: "android-ci"
+      dimensions: "os:Ubuntu-14.04"
+    }
+
+    builders {
       name: "Deterministic Android"
       mixins: "android-ci"
       recipe {
@@ -772,6 +778,15 @@
     }
 
     builders {
+      name: "linux-xenial-rel"
+      mixins: "goma-many-jobs-for-ci"
+      dimensions: "os:Ubuntu-16.04"
+      recipe {
+        properties: "mastername:chromium.linux"
+      }
+    }
+
+    builders {
       name: "Linux Builder (dbg)"
       mixins: "linux-ci"
       dimensions: "cores:32"
@@ -1257,6 +1272,21 @@
     builders { mixins: "android-angle-try" name: "android_angle_deqp_rel_ng" }
     builders {
       mixins: "android-try"
+      name: "android-kitkat-arm-rel"
+      dimensions: "os:Ubuntu-14.04"
+    }
+    builders {
+      mixins: "android-try"
+      name: "linux_android_rel_ng"
+      auto_builder_dimension: NO
+      dimensions: "builder:android-kitkat-arm-rel"
+      dimensions: "os:Ubuntu-14.04"
+      recipe {
+        properties: "buildername:android-kitkat-arm-rel"
+      }
+    }
+    builders {
+      mixins: "android-try"
       name: "android_arm64_dbg_recipe"
       dimensions: "os:Ubuntu-14.04"
     }
@@ -1319,7 +1349,6 @@
     builders { mixins: "linux-try" name: "linux-gcc-rel" }
     builders { mixins: "linux-try" name: "linux-jumbo-rel" }
     builders { mixins: "linux-try" name: "linux-ozone-rel" }
-    builders { mixins: "linux-try" name: "linux_android_rel_ng" }
     builders { mixins: "linux-try" name: "linux_arm" }
     builders { mixins: "linux-try" name: "linux_chromium_analysis" }
     builders { mixins: "linux-try" name: "linux_chromium_archive_rel_ng" }
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index dd2d0b0..dcef344 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -368,7 +368,6 @@
     short_name: "dev"
   }
   builders: {
-    name: "buildbot/chromium.mac/ios-simulator"
     name: "buildbucket/luci.chromium.ci/ios-simulator"
     category: "chromium.mac|ios"
     short_name: "sim"
@@ -404,6 +403,11 @@
     category: "chromium.linux|release"
     short_name: "gcc"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/linux-xenial-rel"
+    category: "chromium.linux|release"
+    short_name: "xen"
+  }
   builders: {
     name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
     category: "chromium.linux|debug|builder"
@@ -836,11 +840,6 @@
     short_name: "dev"
   }
   builders: {
-    name: "buildbot/chromium.mac/ios-simulator"
-    category: "ios"
-    short_name: "sim"
-  }
-  builders: {
     name: "buildbot/chromium.mac/ios-simulator-full-configs"
     category: "ios"
     short_name: "ful"
@@ -871,21 +870,26 @@
     short_name: "bld"
   }
   builders: {
+    name: "buildbucket/luci.chromium.ci/Linux Tests"
+    category: "release"
+    short_name: "tst"
+  }
+  builders: {
     name: "buildbucket/luci.chromium.ci/linux-gcc-rel"
     category: "release"
     short_name: "gcc"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/linux-xenial-rel"
+    category: "release"
+    short_name: "xen"
+  }
   builders: {
     name: "buildbucket/luci.chromium.ci/linux-jumbo-rel"
     category: "jumbo"
     short_name: "jumbo"
   }
   builders: {
-    name: "buildbucket/luci.chromium.ci/Linux Tests"
-    category: "release"
-    short_name: "tst"
-  }
-  builders: {
     name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
     category: "debug|builder"
     short_name: "32"
@@ -1553,17 +1557,6 @@
     category: "chromium.memory|asan|tester|sandbox"
     short_name: "ci"
   }
-
-  builders: {
-    name: "buildbot/chromium.mac/ios-simulator"
-    category: "chromium.mac|ios-simulator"
-    short_name: "bb"
-  }
-  builders: {
-    name: "buildbucket/luci.chromium.ci/ios-simulator"
-    category: "chromium.mac|ios-simulator"
-    short_name: "ci"
-  }
   builders: {
     name: "buildbot/chromium.fyi/Headless Linux (dbg)"
     category: "chromium.fyi|linux|debug"
@@ -1574,6 +1567,17 @@
     category: "chromium.fyi|linux|debug"
     short_name: "ci"
   }
+
+  builders: {
+    name: "buildbucket/luci.chromium.ci/android-kitkat-arm-rel"
+    category: "android|arm|rel"
+    short_name: "ci"
+  }
+  builders: {
+    name: "buildbot/chromium.android/KitKat Phone Tester (rel)"
+    category: "android|arm|rel"
+    short_name: "bb"
+  }
   builders: {
     name: "buildbucket/luci.chromium.ci/Android arm Builder (dbg)"
     category: "android|builder|arm|debug|32"
@@ -1847,6 +1851,7 @@
   }
   builders: {
     name: "buildbot/chromium.android/KitKat Phone Tester (rel)"
+    name: "buildbucket/luci.chromium.ci/android-kitkat-arm-rel"
     category: "on_cq"
     short_name: "K"
   }
@@ -2461,10 +2466,6 @@
     category: "linux"
   }
   builders: {
-    name: "buildbot/chromium.fyi/Linux Xenial"
-    category: "linux"
-  }
-  builders: {
     name: "buildbot/chromium.fyi/Mojo Android"
     category: "mojo"
   }
@@ -3846,6 +3847,7 @@
   }
   builders: {
     name: "buildbot/tryserver.chromium.android/linux_android_rel_ng"
+    name: "buildbucket/luci.chromium.try/android-kitkat-arm-rel"
   }
 }
 
@@ -4384,6 +4386,9 @@
   builder_view_only: true
 
   builders: {
+    name: "buildbucket/luci.chromium.try/android-kitkat-arm-rel"
+  }
+  builders: {
     name: "buildbucket/luci.chromium.try/android_arm64_dbg_recipe"
   }
   builders: {
@@ -4456,9 +4461,6 @@
     name: "buildbucket/luci.chromium.try/linux-ozone-rel"
   }
   builders: {
-    name: "buildbucket/luci.chromium.try/linux_android_rel_ng"
-  }
-  builders: {
     name: "buildbucket/luci.chromium.try/linux_angle_compile_dbg_ng"
   }
   builders: {
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 204064d..20f11b2 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -67,6 +67,7 @@
   triggers: "Android FYI Release (NVIDIA Shield TV)"
   triggers: "Android x64 Builder (dbg)"
   triggers: "Android x86 Builder (dbg)"
+  triggers: "android-kitkat-arm-rel"
   triggers: "Deterministic Android"
   triggers: "Deterministic Android (dbg)"
   triggers: "Optional Android Release (Nexus 5X)"
@@ -100,6 +101,7 @@
   triggers: "linux-gcc-rel"
   triggers: "linux-jumbo-rel"
   triggers: "linux-ozone-rel"
+  triggers: "linux-xenial-rel"
   triggers: "Linux ASan LSan Builder"
   triggers: "Linux Builder"
   triggers: "Linux Builder (dbg)"
@@ -356,6 +358,16 @@
 }
 
 job {
+  id: "android-kitkat-arm-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "android-kitkat-arm-rel"
+  }
+}
+
+job {
   id: "Deterministic Android"
   acl_sets: "default"
   buildbucket: {
@@ -869,6 +881,26 @@
 }
 
 job {
+  id: "linux-ozone-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-ozone-rel"
+  }
+}
+
+job {
+  id: "linux-xenial-rel"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "linux-xenial-rel"
+  }
+}
+
+job {
   id: "Linux ASan LSan Builder"
   acl_sets: "default"
   buildbucket: {
@@ -994,16 +1026,6 @@
   }
 }
 
-job {
-  id: "linux-ozone-rel"
-  acl_sets: "default"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "linux-ozone-rel"
-  }
-}
-
 ################################################################################
 # Mac Builders. Sorted alphabetically.
 ################################################################################
diff --git a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.h b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.h
index ebb08a1f..d4ab5f9 100644
--- a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.h
+++ b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.h
@@ -6,13 +6,9 @@
 #define IOS_CHROME_BROWSER_ITUNES_LINKS_ITUNES_LINKS_HANDLER_TAB_HELPER_H_
 
 #include "base/macros.h"
-#include "ios/web/public/web_state/web_state_observer.h"
+#import "ios/web/public/web_state/web_state_policy_decider.h"
 #include "ios/web/public/web_state/web_state_user_data.h"
 
-namespace web {
-class WebStatePolicyDecider;
-}
-
 // Enum for the IOS.StoreKit.ITunesURLsHandlingResult UMA histogram to report
 // the results of the StoreKit handling.
 // These values are persisted to logs. Entries should not be renumbered and
@@ -31,28 +27,26 @@
   kCount
 };
 
-// TabHelper which handles navigation to iTunes links.
-// If a navigation to web page for a product in iTunes App Store happens while
-// in non off the record browsing mode, this helper will use StoreKitTabHelper
-// to present the information of that product. The goal of this class is to
-// workaround a bug where appstore website serves the wrong content for
-// itunes.apple.com pages, see http://crbug.com/623016.
+// A Tab helper for iTunes Apps URLs handling.
+// If a navigation to web page for a supported product in iTunes App Store
+// happens while in non off the record browsing mode, this helper will use
+// StoreKitTabHelper to present the information of that product. The goal of
+// this class is to workaround a bug where appstore website serves the wrong
+// content for itunes.apple.com pages, see http://crbug.com/623016.
 class ITunesLinksHandlerTabHelper
-    : public web::WebStateObserver,
+    : public web::WebStatePolicyDecider,
       public web::WebStateUserData<ITunesLinksHandlerTabHelper> {
  public:
   ~ITunesLinksHandlerTabHelper() override;
   explicit ITunesLinksHandlerTabHelper(web::WebState* web_state);
+  // web::WebStatePolicyDecider implementation
+  bool ShouldAllowRequest(NSURLRequest* request,
+                          ui::PageTransition transition,
+                          bool from_main_frame) override;
 
  private:
-  // web::WebStateObserver implementation
-  void DidFinishNavigation(web::WebState* web_state,
-                           web::NavigationContext* navigation_context) override;
-  void WebStateDestroyed(web::WebState* web_state) override;
-
-  // PolicyDecider instance that will be initialized with the
-  // ITunesLinkHandlerTabHelper object, and destroyed with it.
-  std::unique_ptr<web::WebStatePolicyDecider> policy_decider_;
+  // Opens the StoreKit for the given iTunes app |url|.
+  void HandleITunesUrl(const GURL& url);
 
   DISALLOW_COPY_AND_ASSIGN(ITunesLinksHandlerTabHelper);
 };
diff --git a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.mm b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.mm
index 2d343ffe2..d3bb5642 100644
--- a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.mm
+++ b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper.mm
@@ -18,7 +18,6 @@
 #include "ios/web/public/browser_state.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
-#import "ios/web/public/web_state/navigation_context.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
 #include "net/base/filename_util.h"
 #import "net/base/mac/url_conversions.h"
@@ -78,67 +77,29 @@
   return params_dictionary;
 }
 
-// This class handles requests & responses that involve iTunes product links.
-class ITunesLinksHandlerWebStatePolicyDecider
-    : public web::WebStatePolicyDecider {
- public:
-  explicit ITunesLinksHandlerWebStatePolicyDecider(web::WebState* web_state)
-      : web::WebStatePolicyDecider(web_state) {}
-
-  // web::WebStatePolicyDecider implementation
-  bool ShouldAllowResponse(NSURLResponse* response,
-                           bool for_main_frame) override {
-    // Don't allow rendering responses from URLs that can be handled by
-    // iTunesLinksHandler unless it's on iframe or the browsing mode is off the
-    // record.
-    return web_state()->GetBrowserState()->IsOffTheRecord() ||
-           !for_main_frame || !CanHandleUrl(net::GURLWithNSURL(response.URL));
-  }
-
-  bool ShouldAllowRequest(NSURLRequest* request,
-                          ui::PageTransition transition,
-                          bool from_main_frame) override {
-    // Only consider blocking the request if it's not of the record mode.
-    if (web_state()->GetBrowserState()->IsOffTheRecord())
-      return true;
-    web::NavigationItem* pending_item =
-        web_state()->GetNavigationManager()->GetPendingItem();
-
-    if (!pending_item)
-      return true;
-    // If the pending item URL is http iTunes URL that can be handled, but the
-    // request URL is not http URL, then there was a redirect to an external
-    // application and request should be blocked to be able to show the store
-    // kit later.
-    GURL pending_item_url = pending_item->GetURL();
-    GURL request_url = net::GURLWithNSURL(request.URL);
-    return !CanHandleUrl(pending_item_url) || request_url.SchemeIsHTTPOrHTTPS();
-  }
-
-  // Returns true, if iTunesLinksHandler can handle the given |url|.
-  static bool CanHandleUrl(const GURL& url) {
-    if (!IsITunesProductUrl(url))
-      return false;
-    // Valid iTunes URL structure:
-    // DOMAIN/OPTIONAL_REGION_CODE/MEDIA_TYPE/MEDIA_NAME/ID?PARAMETERS
-    // Check the URL media type, to determine if it is supported.
-    base::FilePath path;
-    if (!net::FileURLToFilePath(url, &path))
-      return false;
-    std::vector<base::FilePath::StringType> path_components;
-    path.GetComponents(&path_components);
-    // GetComponents considers "/" as the first component.
-    if (path_components.size() < kITunesUrlPathMinComponentsCount)
-      return false;
-    size_t media_type_index = kITunesUrlMediaTypeComponentDefaultIndex;
-    DCHECK(media_type_index > 0);
-    // If there is no reigon code in the URL then media type has to appear
-    // earlier in the URL.
-    if (path_components[kITunesUrlRegionComponentDefaultIndex].size() != 2)
-      media_type_index--;
-    return path_components[media_type_index] == kITunesAppPathIdentifier;
-  }
-};
+// Returns true, if ITunesLinksHandlerTabHelper can handle the given |url|.
+bool CanHandleUrl(const GURL& url) {
+  if (!IsITunesProductUrl(url))
+    return false;
+  // Valid iTunes URL structure:
+  // DOMAIN/OPTIONAL_REGION_CODE/MEDIA_TYPE/MEDIA_NAME/ID?PARAMETERS
+  // Check the URL media type, to determine if it is supported.
+  base::FilePath path;
+  if (!net::FileURLToFilePath(url, &path))
+    return false;
+  std::vector<base::FilePath::StringType> path_components;
+  path.GetComponents(&path_components);
+  // GetComponents considers "/" as the first component.
+  if (path_components.size() < kITunesUrlPathMinComponentsCount)
+    return false;
+  size_t media_type_index = kITunesUrlMediaTypeComponentDefaultIndex;
+  DCHECK(media_type_index > 0);
+  // If there is no reigon code in the URL then media type has to appear
+  // earlier in the URL.
+  if (path_components[kITunesUrlRegionComponentDefaultIndex].size() != 2)
+    media_type_index--;
+  return path_components[media_type_index] == kITunesAppPathIdentifier;
+}
 
 }  // namespace
 
@@ -146,39 +107,37 @@
 
 ITunesLinksHandlerTabHelper::ITunesLinksHandlerTabHelper(
     web::WebState* web_state)
-    : policy_decider_(std::make_unique<ITunesLinksHandlerWebStatePolicyDecider>(
-          web_state)) {
-  web_state->AddObserver(this);
+    : web::WebStatePolicyDecider(web_state) {}
+
+bool ITunesLinksHandlerTabHelper::ShouldAllowRequest(
+    NSURLRequest* request,
+    ui::PageTransition transition,
+    bool from_main_frame) {
+  // Don't Handle URLS in Off The record mode as this will open StoreKit with
+  // Users' iTunes account. Also don't Handle requests from iframe because they
+  // may be spam, and they will be handled by other policy deciders.
+  if (web_state()->GetBrowserState()->IsOffTheRecord() || !from_main_frame)
+    return true;
+
+  GURL request_url = net::GURLWithNSURL(request.URL);
+  if (!CanHandleUrl(request_url))
+    return true;
+
+  HandleITunesUrl(request_url);
+  return false;
 }
 
-// WebStateObserver
-void ITunesLinksHandlerTabHelper::DidFinishNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  // Don't handle iTunse URL in off the record mode.
-  if (web_state->GetBrowserState()->IsOffTheRecord())
-    return;
-
-  GURL url = navigation_context->GetUrl();
-  // Whenever a navigation to iTunes product url is finished, launch StoreKit.
-  if (ITunesLinksHandlerWebStatePolicyDecider::CanHandleUrl(url)) {
-    ITunesUrlsStoreKitHandlingResult handling_result =
-        ITunesUrlsStoreKitHandlingResult::kSingleAppUrlHandled;
-    // If the url is iTunes product url, then this navigation should not be
-    // committed, as the policy decider's ShouldAllowResponse will return false.
-    DCHECK(!navigation_context->HasCommitted());
-    StoreKitTabHelper* tab_helper = StoreKitTabHelper::FromWebState(web_state);
-    if (tab_helper) {
-      base::RecordAction(
-          base::UserMetricsAction("ITunesLinksHandler_StoreKitLaunched"));
-      tab_helper->OpenAppStore(ExtractITunesProductParameters(url));
-    } else {
-      handling_result = ITunesUrlsStoreKitHandlingResult::kUrlHandlingFailed;
-    }
-    RecordStoreKitHandlingResult(handling_result);
+// private
+void ITunesLinksHandlerTabHelper::HandleITunesUrl(const GURL& url) {
+  ITunesUrlsStoreKitHandlingResult handling_result =
+      ITunesUrlsStoreKitHandlingResult::kSingleAppUrlHandled;
+  StoreKitTabHelper* tab_helper = StoreKitTabHelper::FromWebState(web_state());
+  if (tab_helper) {
+    base::RecordAction(
+        base::UserMetricsAction("ITunesLinksHandler_StoreKitLaunched"));
+    tab_helper->OpenAppStore(ExtractITunesProductParameters(url));
+  } else {
+    handling_result = ITunesUrlsStoreKitHandlingResult::kUrlHandlingFailed;
   }
-}
-
-void ITunesLinksHandlerTabHelper::WebStateDestroyed(web::WebState* web_state) {
-  web_state->RemoveObserver(this);
+  RecordStoreKitHandlingResult(handling_result);
 }
diff --git a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper_unittest.mm b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper_unittest.mm
index 0386eb5..b05cd208 100644
--- a/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper_unittest.mm
+++ b/ios/chrome/browser/itunes_links/itunes_links_handler_tab_helper_unittest.mm
@@ -6,19 +6,15 @@
 
 #import <Foundation/Foundation.h>
 
-#include "base/observer_list.h"
 #include "base/test/histogram_tester.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
 #import "ios/chrome/test/fakes/fake_store_kit_launcher.h"
-#import "ios/web/public/test/fakes/fake_navigation_context.h"
-#import "ios/web/public/test/fakes/test_navigation_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
-#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -37,125 +33,113 @@
     web_state_.SetBrowserState(
         chrome_browser_state_->GetOriginalChromeBrowserState());
     StoreKitTabHelper::CreateForWebState(&web_state_);
-    std::unique_ptr<web::TestNavigationManager> test_navigation_manager =
-        std::make_unique<web::TestNavigationManager>();
-    navigation_manager_ = test_navigation_manager.get();
-    web_state_.SetNavigationManager(std::move(test_navigation_manager));
     ITunesLinksHandlerTabHelper::CreateForWebState(&web_state_);
     StoreKitTabHelper::FromWebState(&web_state_)->SetLauncher(fake_launcher_);
   }
 
-  // Tries to finish navigation with the given |url_string| and returns true if
-  // store kit was launched.
-  bool VerifyStoreKitLaunched(const std::string& url_string) {
+  // Calls ShouldAllowRequest for a request with the given |url_string| and
+  // returns true if storekit was launched.
+  bool VerifyStoreKitLaunched(NSString* url_string, bool main_frame) {
     fake_launcher_.launchedProductID = nil;
     fake_launcher_.launchedProductParams = nil;
-    web::FakeNavigationContext context;
-    context.SetUrl(GURL(url_string));
-    web_state_.OnNavigationFinished(&context);
-    return fake_launcher_.launchedProductID != nil ||
-           fake_launcher_.launchedProductParams != nil;
-  }
-
-  // Checks that given the pending item URL & the request URL if
-  // ITunesHandlerPolicyDecider will allow the request.
-  bool ShouldAllowRequest(NSString* url_string,
-                          std::string const& pending_item_url) {
-    std::unique_ptr<web::NavigationItem> pending_item;
-    if (!pending_item_url.empty()) {
-      pending_item = web::NavigationItem::Create();
-      pending_item->SetURL(GURL(pending_item_url));
-    }
-    navigation_manager_->SetPendingItem(pending_item.get());
-
-    return web_state_.ShouldAllowRequest(
+    bool request_allowed = web_state_.ShouldAllowRequest(
         [NSURLRequest requestWithURL:[NSURL URLWithString:url_string]],
-        ui::PageTransition::PAGE_TRANSITION_LINK, /*from_main_frame=*/true);
-  }
-
-  // Checks that given the pending item URL & the request URL if
-  // ITunesHandlerPolicyDecider will allow the request.
-  bool ShouldAllowResponse(NSString* url_string, bool main_frame) {
-    NSURLResponse* response =
-        [[NSURLResponse alloc] initWithURL:[NSURL URLWithString:url_string]
-                                  MIMEType:@"text/html"
-                     expectedContentLength:0
-                          textEncodingName:nil];
-    return web_state_.ShouldAllowResponse(response, main_frame);
+        ui::PageTransition::PAGE_TRANSITION_LINK, main_frame);
+    return !request_allowed && (fake_launcher_.launchedProductID != nil ||
+                                fake_launcher_.launchedProductParams != nil);
   }
 
   web::TestWebThreadBundle thread_bundle_;
-  web::TestNavigationManager* navigation_manager_;
   FakeStoreKitLauncher* fake_launcher_;
   web::TestWebState web_state_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   base::HistogramTester histogram_tester_;
 };
 
+// Verifies that iTunes URLs are not handled when in off the record mode.
+TEST_F(ITunesLinksHandlerTabHelperTest, NoHandlingInOffTheRecordMode) {
+  NSString* url = @"http://itunes.apple.com/us/app/app_name/id123";
+  EXPECT_TRUE(VerifyStoreKitLaunched(url, /*main_frame=*/true));
+  web_state_.SetBrowserState(
+      chrome_browser_state_->GetOffTheRecordChromeBrowserState());
+  EXPECT_FALSE(VerifyStoreKitLaunched(url, /*main_frame=*/true));
+}
+
+// Verifies that iTunes URLs are not handled when the request is from iframe.
+TEST_F(ITunesLinksHandlerTabHelperTest, NoHandlingInIframes) {
+  EXPECT_TRUE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/us/app/app_name/id123", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/us/app/app_name/id123", /*main_frame=*/false));
+  EXPECT_TRUE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/app/bar/id243?at=12312", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/app/bar/id243?at=12312", /*main_frame=*/false));
+}
+
 // Verifies that navigating to non iTunes product URLs, or not supported iTunes
 // product type URLs does not launch storekit.
 TEST_F(ITunesLinksHandlerTabHelperTest, NonMatchingUrlsDoesntLaunchStoreKit) {
-  EXPECT_FALSE(VerifyStoreKitLaunched(""));
-  EXPECT_FALSE(VerifyStoreKitLaunched("foobar"));
-  EXPECT_FALSE(VerifyStoreKitLaunched("foo://bar"));
-  EXPECT_FALSE(VerifyStoreKitLaunched("http://foo"));
-  EXPECT_FALSE(VerifyStoreKitLaunched("http://foo?bar#qux"));
-  EXPECT_FALSE(VerifyStoreKitLaunched("http://foo.bar/qux"));
+  EXPECT_FALSE(VerifyStoreKitLaunched(@"", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(@"foobar", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(@"foo://bar", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(@"http://foo", /*main_frame=*/true));
   EXPECT_FALSE(
-      VerifyStoreKitLaunched("http://geo.itunes.apple.com/de/genre/apps/"));
-  EXPECT_FALSE(VerifyStoreKitLaunched(
-      "https://itunes.apple.com/us/tv-show/theshow/id1232"));
+      VerifyStoreKitLaunched(@"http://foo?bar#qux", /*main_frame=*/true));
   EXPECT_FALSE(
-      VerifyStoreKitLaunched("http://itunes.apple.com/podcast/id12345"));
+      VerifyStoreKitLaunched(@"http://foo.bar/qux", /*main_frame=*/true));
   EXPECT_FALSE(VerifyStoreKitLaunched(
-      "itms-apps://itunes.apple.com/us/app/appname/id123"));
+      @"http://geo.itunes.apple.com/de/genre/apps/", /*main_frame=*/true));
   EXPECT_FALSE(VerifyStoreKitLaunched(
-      "http://itunes.apple.com/us/movie/testmovie/id12345"));
-  EXPECT_FALSE(
-      VerifyStoreKitLaunched("http://itunes.apple.com/app-bundle/id12345"));
+      @"https://itunes.apple.com/us/tv-show/theshow/id1232",
+      /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/podcast/id12345", /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"itms-apps://itunes.apple.com/us/app/appname/id123",
+      /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/us/movie/testmovie/id12345",
+      /*main_frame=*/true));
+  EXPECT_FALSE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/app-bundle/id12345", /*main_frame=*/true));
   histogram_tester_.ExpectTotalCount(kITunesURLsHandlingResultHistogram, 0);
 }
 
-// Verifies that iTunes URLs are not handled when in off the record mode.
-TEST_F(ITunesLinksHandlerTabHelperTest, NoHandlingInOffTheRecordMode) {
-  std::string url = "http://itunes.apple.com/us/app/app_name/id123";
-  EXPECT_TRUE(VerifyStoreKitLaunched(url));
-  web_state_.SetBrowserState(
-      chrome_browser_state_->GetOffTheRecordChromeBrowserState());
-  EXPECT_FALSE(VerifyStoreKitLaunched(url));
-}
-
 // Verifies that navigating to URLs for a product hosted on iTunes AppStore
 // with supported media type launches storekit.
 TEST_F(ITunesLinksHandlerTabHelperTest, MatchingUrlsLaunchesStoreKit) {
-  EXPECT_TRUE(
-      VerifyStoreKitLaunched("http://itunes.apple.com/us/app/app_name/id123"));
+  EXPECT_TRUE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/us/app/app_name/id123", /*main_frame=*/true));
   NSString* product_id = @"id";
   NSString* af_tkn = @"at";
   NSDictionary* expected_params = @{product_id : @"123"};
 
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
-
-  EXPECT_TRUE(VerifyStoreKitLaunched("http://itunes.apple.com/app/bar/id123?"));
+  EXPECT_TRUE(VerifyStoreKitLaunched(@"http://itunes.apple.com/app/bar/id123?",
+                                     /*main_frame=*/true));
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
 
   EXPECT_TRUE(VerifyStoreKitLaunched(
-      "http://foo.itunes.apple.com/app/test/id123?qux&baz#foo"));
+      @"http://foo.itunes.apple.com/app/test/id123?qux&baz#foo",
+      /*main_frame=*/true));
   expected_params = @{product_id : @"123", @"qux" : @"", @"baz" : @""};
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
 
-  EXPECT_TRUE(
-      VerifyStoreKitLaunched("http://itunes.apple.com/app/bar/id243?at=12312"));
+  EXPECT_TRUE(VerifyStoreKitLaunched(
+      @"http://itunes.apple.com/app/bar/id243?at=12312", /*main_frame=*/true));
   expected_params = @{product_id : @"243", af_tkn : @"12312"};
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
 
   EXPECT_TRUE(VerifyStoreKitLaunched(
-      "http://itunes.apple.com/app/bar/idabc?at=213&ct=123"));
+      @"http://itunes.apple.com/app/bar/idabc?at=213&ct=123",
+      /*main_frame=*/true));
   expected_params = @{product_id : @"abc", af_tkn : @"213", @"ct" : @"123"};
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
 
   EXPECT_TRUE(VerifyStoreKitLaunched(
-      "http://itunes.apple.com/de/app/bar/id123?at=2&uo=4#foo"));
+      @"http://itunes.apple.com/de/app/bar/id123?at=2&uo=4#foo",
+      /*main_frame=*/true));
   expected_params = @{product_id : @"123", af_tkn : @"2", @"uo" : @"4"};
   EXPECT_NSEQ(expected_params, fake_launcher_.launchedProductParams);
 
@@ -166,50 +150,3 @@
       6);
   histogram_tester_.ExpectTotalCount(kITunesURLsHandlingResultHistogram, 6);
 }
-
-// Verifies that ITunesLinkHandlerPolicyDecider don't allow redirects to Apple
-// appstore when the original request link is supported http iTunes product URL.
-TEST_F(ITunesLinksHandlerTabHelperTest, TestPolicyDeciderShouldAllowRequest) {
-  EXPECT_FALSE(ShouldAllowRequest(
-      /*url_string=*/@"itms://itunes.apple.com/us/app/name/id12345",
-      /*pending_item_url=*/"http://itunes.apple.com/us/app/name/id12345"));
-  EXPECT_TRUE(ShouldAllowRequest(
-      /*url_string=*/@"itms://itunes.apple.com/us/app/name/id12345",
-      /*pending_item_url=*/"http://foo.bar"));
-  EXPECT_TRUE(ShouldAllowRequest(
-      /*url_string=*/@"http://itunes.apple.com/us/app/name/id12345",
-      /*pending_item_url=*/""));
-  EXPECT_TRUE(ShouldAllowRequest(
-      /*url_string=*/@"itms-apps://itunes.apple.com/us/podcast/name/id123",
-      /*pending_item_url=*/"https://itunes.apple.com/us/podcast/name/id123"));
-  EXPECT_TRUE(ShouldAllowRequest(
-      /*url_string=*/@"https://itunes.apple.com/app/test/id12345",
-      /*pending_item_url=*/"https://foo.bar"));
-}
-
-// Verifies that ITunesLinkHandlerPolicyDecider block response from supported
-// http iTunes product URL.
-TEST_F(ITunesLinksHandlerTabHelperTest, TestPolicyDeciderShouldAllowResponse) {
-  EXPECT_TRUE(ShouldAllowResponse(/*url_string=*/@"https://itunes.apple.com/",
-                                  /*main_frame=*/true));
-  EXPECT_FALSE(ShouldAllowResponse(
-      /*url_string=*/@"http://itunes.apple.com/app/test/id1234",
-      /*main_frame=*/true));
-  // If the response is for subframe, it should be allowed.
-  EXPECT_TRUE(ShouldAllowResponse(
-      /*url_string=*/@"http://itunes.apple.com/app/test/id1234",
-      /*main_frame=*/false));
-
-  EXPECT_TRUE(ShouldAllowResponse(
-      /*url_string=*/@"https://itunes.apple.com/us/podcast/name/id123",
-      /*main_frame=*/true));
-  EXPECT_FALSE(ShouldAllowResponse(
-      /*url_string=*/@"http://itunes.apple.com/de/app/bar/id123?qux",
-      /*main_frame=*/true));
-
-  EXPECT_TRUE(ShouldAllowResponse(
-      /*url_string=*/@"itms-apps://itunes.apple.com/de/app/bar/id123?qux",
-      /*main_frame=*/true));
-  EXPECT_TRUE(ShouldAllowResponse(/*url_string=*/@"http://foo.bar/qux",
-                                  /*main_frame=*/true));
-}
diff --git a/ios/chrome/browser/ui/bookmarks/BUILD.gn b/ios/chrome/browser/ui/bookmarks/BUILD.gn
index 83e09b7..fa52c64 100644
--- a/ios/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/BUILD.gn
@@ -17,6 +17,9 @@
     "bookmark_folder_table_view_cell.mm",
     "bookmark_folder_view_controller.h",
     "bookmark_folder_view_controller.mm",
+    "bookmark_home_consumer.h",
+    "bookmark_home_mediator.h",
+    "bookmark_home_mediator.mm",
     "bookmark_home_shared_state.h",
     "bookmark_home_shared_state.mm",
     "bookmark_home_view_controller.h",
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_consumer.h b/ios/chrome/browser/ui/bookmarks/bookmark_home_consumer.h
new file mode 100644
index 0000000..e6803a0
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_consumer.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_BOOKMARKS_BOOKMARK_HOME_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_HOME_CONSUMER_H_
+
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_consumer.h"
+
+// BookmarkHomeConsumer provides methods that allow mediators to update the UI.
+@protocol BookmarkHomeConsumer<ChromeTableViewConsumer>
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_HOME_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.h b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.h
new file mode 100644
index 0000000..e746582
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_HOME_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_HOME_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+@protocol BookmarkHomeConsumer;
+@class BookmarkHomeSharedState;
+
+// BookmarkHomeMediator manages model interactions for the
+// BookmarkHomeViewController.
+@interface BookmarkHomeMediator : NSObject
+
+@property(nonatomic, weak) id<BookmarkHomeConsumer> consumer;
+
+- (instancetype)initWithSharedState:(BookmarkHomeSharedState*)sharedState
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Starts this mediator. Populates the table view model with current data and
+// begins listening for backend model updates.
+- (void)startMediating;
+
+// Stops mediating and disconnects from backend models.
+- (void)disconnect;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_HOME_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
new file mode 100644
index 0000000..a3f0dbb2
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
@@ -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 "ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_shared_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface BookmarkHomeMediator ()
+
+@property(nonatomic, strong) BookmarkHomeSharedState* sharedState;
+
+@end
+
+@implementation BookmarkHomeMediator
+@synthesize consumer = _consumer;
+@synthesize sharedState = _sharedState;
+
+- (instancetype)initWithSharedState:(BookmarkHomeSharedState*)sharedState {
+  if ((self = [super init])) {
+    _sharedState = sharedState;
+  }
+  return self;
+}
+
+- (void)startMediating {
+  DCHECK(self.consumer);
+  DCHECK(self.sharedState);
+}
+
+- (void)disconnect {
+  self.consumer = nil;
+  self.sharedState = nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index 145647f..7188188 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -17,6 +17,8 @@
 #import "ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_consumer.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_shared_state.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_waiting_view.h"
 #include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
@@ -79,6 +81,7 @@
     BookmarkEditViewControllerDelegate,
     BookmarkFolderEditorViewControllerDelegate,
     BookmarkFolderViewControllerDelegate,
+    BookmarkHomeConsumer,
     BookmarkHomeSharedStateObserver,
     BookmarkModelBridgeObserver,
     BookmarkPromoControllerDelegate,
@@ -108,9 +111,15 @@
 // The user's browser state model used.
 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
 
+// The mediator that provides data for this view controller.
+@property(nonatomic, strong) BookmarkHomeMediator* mediator;
+
 // The main view showing all the bookmarks.
 @property(nonatomic, strong) BookmarkTableView* bookmarksTableView;
 
+// The table view's styler.
+@property(nonatomic, strong) ChromeTableViewStyler* tableViewStyler;
+
 // The view controller used to pick a folder in which to move the selected
 // bookmarks.
 @property(nonatomic, strong) BookmarkFolderViewController* folderSelector;
@@ -169,6 +178,8 @@
 @synthesize cachedContentPosition = _cachedContentPosition;
 @synthesize isReconstructingFromCache = _isReconstructingFromCache;
 @synthesize sharedState = _sharedState;
+@synthesize mediator = _mediator;
+@synthesize tableViewStyler = _tableViewStyler;
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -200,6 +211,13 @@
   return self;
 }
 
+- (void)dealloc {
+  [self.mediator disconnect];
+  [self removeKeyboardObservers];
+  _sharedState.tableView.dataSource = nil;
+  _sharedState.tableView.delegate = nil;
+}
+
 - (void)setRootNode:(const bookmarks::BookmarkNode*)rootNode {
   _rootNode = rootNode;
 }
@@ -278,9 +296,35 @@
   [self.view addSubview:self.bookmarksTableView];
 
   // Configure the table view.
+  self.tableViewStyler = [[ChromeTableViewStyler alloc] init];
+  self.sharedState.tableView.accessibilityIdentifier = @"bookmarksTableView";
+  if (@available(iOS 11.0, *)) {
+    self.sharedState.tableView.contentInsetAdjustmentBehavior =
+        UIScrollViewContentInsetAdjustmentNever;
+  }
+  self.sharedState.tableView.estimatedRowHeight =
+      [BookmarkHomeSharedState cellHeightPt];
+  self.sharedState.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+  self.sharedState.tableView.allowsMultipleSelectionDuringEditing = YES;
+
+  UILongPressGestureRecognizer* longPressRecognizer =
+      [[UILongPressGestureRecognizer alloc]
+          initWithTarget:self
+                  action:@selector(handleLongPress:)];
+  longPressRecognizer.numberOfTouchesRequired = 1;
+  longPressRecognizer.delegate = self;
+  [self.sharedState.tableView addGestureRecognizer:longPressRecognizer];
+
+  // Create the mediator and hook up the table view.
+  self.mediator =
+      [[BookmarkHomeMediator alloc] initWithSharedState:self.sharedState];
+  self.mediator.consumer = self;
+  [self.mediator startMediating];
   self.sharedState.tableView.dataSource = self;
   self.sharedState.tableView.delegate = self;
 
+  [self registerForKeyboardNotifications];
+
   // After the table view has been added.
   [self setupNavigationBar];
 
@@ -316,6 +360,22 @@
                                                      .contentPosition)];
 }
 
+#pragma mark - BookmarkHomeConsumer
+
+- (void)reconfigureCellsForItems:(NSArray*)items {
+  for (TableViewItem* item in items) {
+    NSIndexPath* indexPath =
+        [self.sharedState.tableViewModel indexPathForItem:item];
+    UITableViewCell* cell =
+        [self.sharedState.tableView cellForRowAtIndexPath:indexPath];
+
+    // |cell| may be nil if the row is not currently on screen.
+    if (cell) {
+      [item configureCell:cell withStyler:self.tableViewStyler];
+    }
+  }
+}
+
 #pragma mark - BookmarkPromoControllerDelegate
 
 - (void)promoStateChanged:(BOOL)promoEnabled {
@@ -1248,12 +1308,114 @@
   return alert;
 }
 
-#pragma mark - UIGestureRecognizerDelegate
+#pragma mark - UIGestureRecognizerDelegate and gesture handling
 
 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer {
-  DCHECK(gestureRecognizer ==
-         self.navigationController.interactivePopGestureRecognizer);
-  return self.navigationController.viewControllers.count > 1;
+  if (gestureRecognizer ==
+      self.navigationController.interactivePopGestureRecognizer) {
+    return self.navigationController.viewControllers.count > 1;
+  }
+  return YES;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
+       shouldReceiveTouch:(UITouch*)touch {
+  // Ignore long press in edit mode.
+  if (self.sharedState.currentlyInEditMode) {
+    return NO;
+  }
+  return YES;
+}
+
+- (void)handleLongPress:(UILongPressGestureRecognizer*)gestureRecognizer {
+  if (self.sharedState.currentlyInEditMode ||
+      gestureRecognizer.state != UIGestureRecognizerStateBegan) {
+    return;
+  }
+  CGPoint touchPoint =
+      [gestureRecognizer locationInView:self.sharedState.tableView];
+  NSIndexPath* indexPath =
+      [self.sharedState.tableView indexPathForRowAtPoint:touchPoint];
+  if (indexPath == nil || [self.sharedState.tableViewModel
+                              sectionIdentifierForSection:indexPath.section] !=
+                              BookmarkHomeSectionIdentifierBookmarks) {
+    return;
+  }
+
+  const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+  // Disable the long press gesture if it is a permanent node (not an URL or
+  // Folder).
+  if (!node || ![self isUrlOrFolder:node]) {
+    return;
+  }
+
+  [self bookmarkTableView:self.bookmarksTableView showContextMenuForNode:node];
+}
+
+#pragma mark - Keyboard
+
+- (void)registerForKeyboardNotifications {
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(keyboardWasShown:)
+             name:UIKeyboardDidShowNotification
+           object:nil];
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(keyboardWillBeHidden:)
+             name:UIKeyboardWillHideNotification
+           object:nil];
+}
+
+- (void)removeKeyboardObservers {
+  NSNotificationCenter* notificationCenter =
+      [NSNotificationCenter defaultCenter];
+  [notificationCenter removeObserver:self
+                                name:UIKeyboardDidShowNotification
+                              object:nil];
+  [notificationCenter removeObserver:self
+                                name:UIKeyboardWillHideNotification
+                              object:nil];
+}
+
+// Called when the UIKeyboardDidShowNotification is sent
+- (void)keyboardWasShown:(NSNotification*)aNotification {
+  if (![self isAtTopOfNavigation:self.bookmarksTableView]) {
+    return;
+  }
+  NSDictionary* info = [aNotification userInfo];
+  CGFloat keyboardTop =
+      [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
+  CGFloat tableBottom = CGRectGetMaxY([self.bookmarksTableView
+      convertRect:self.sharedState.tableView.frame
+           toView:nil]);
+  CGFloat shiftY =
+      tableBottom - keyboardTop + [BookmarkHomeSharedState keyboardSpacingPt];
+
+  if (shiftY >= 0) {
+    UIEdgeInsets previousContentInsets =
+        self.sharedState.tableView.contentInset;
+    // Shift the content inset to prevent the editing content from being hidden
+    // by the keyboard.
+    UIEdgeInsets contentInsets =
+        UIEdgeInsetsMake(previousContentInsets.top, 0.0, shiftY, 0.0);
+    self.sharedState.tableView.contentInset = contentInsets;
+    self.sharedState.tableView.scrollIndicatorInsets = contentInsets;
+  }
+}
+
+// Called when the UIKeyboardWillHideNotification is sent
+- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
+  if (![self isAtTopOfNavigation:self.bookmarksTableView]) {
+    return;
+  }
+  UIEdgeInsets previousContentInsets = self.sharedState.tableView.contentInset;
+  // Restore the content inset now that the keyboard has been hidden.
+  UIEdgeInsets contentInsets =
+      UIEdgeInsetsMake(previousContentInsets.top, 0, 0, 0);
+  self.sharedState.tableView.contentInset = contentInsets;
+  self.sharedState.tableView.scrollIndicatorInsets = contentInsets;
 }
 
 #pragma mark - SigninPresenter
@@ -1323,7 +1485,7 @@
   UITableViewCell* cell = [self.sharedState.tableView
       dequeueReusableCellWithIdentifier:reuseIdentifier
                            forIndexPath:indexPath];
-  [item configureCell:cell withStyler:[[ChromeTableViewStyler alloc] init]];
+  [item configureCell:cell withStyler:self.tableViewStyler];
 
   if (item.type == BookmarkHomeItemTypeBookmark) {
     BookmarkHomeNodeItem* nodeItem =
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
index e06f718..755be44d 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_table_view.mm
@@ -9,6 +9,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/favicon/core/fallback_url_util.h"
+#include "components/favicon/core/favicon_server_fetcher_params.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "components/favicon_base/fallback_icon_style.h"
 #include "components/favicon_base/favicon_types.h"
@@ -144,39 +145,18 @@
     // Create and setup tableview.
     self.sharedState.tableView =
         [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
-    self.sharedState.tableView.accessibilityIdentifier = @"bookmarksTableView";
-    if (@available(iOS 11.0, *)) {
-      self.sharedState.tableView.contentInsetAdjustmentBehavior =
-          UIScrollViewContentInsetAdjustmentNever;
-    }
-    self.sharedState.tableView.estimatedRowHeight =
-        [BookmarkHomeSharedState cellHeightPt];
-    self.sharedState.tableView.separatorStyle =
-        UITableViewCellSeparatorStyleNone;
     // Remove extra rows.
     self.sharedState.tableView.autoresizingMask =
         UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-    self.sharedState.tableView.allowsMultipleSelectionDuringEditing = YES;
-    UILongPressGestureRecognizer* longPressRecognizer =
-        [[UILongPressGestureRecognizer alloc]
-            initWithTarget:self
-                    action:@selector(handleLongPress:)];
-    longPressRecognizer.numberOfTouchesRequired = 1;
-    longPressRecognizer.delegate = self;
-    [self.sharedState.tableView addGestureRecognizer:longPressRecognizer];
     [self addSubview:self.sharedState.tableView];
     [self bringSubviewToFront:self.sharedState.tableView];
 
-    [self registerForKeyboardNotifications];
     [self showEmptyOrLoadingSpinnerBackgroundIfNeeded];
   }
   return self;
 }
 
 - (void)dealloc {
-  [self removeKeyboardObservers];
-  _sharedState.tableView.dataSource = nil;
-  _sharedState.tableView.delegate = nil;
   _faviconTaskTracker.TryCancelAll();
 }
 
@@ -430,44 +410,6 @@
   [self loadFaviconAtIndexPath:indexPath continueToGoogleServer:NO];
 }
 
-#pragma mark - Gesture recognizer
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
-       shouldReceiveTouch:(UITouch*)touch {
-  // Ignore long press in edit mode.
-  if (self.sharedState.currentlyInEditMode) {
-    return NO;
-  }
-  return YES;
-}
-
-#pragma mark - Private
-
-- (void)handleLongPress:(UILongPressGestureRecognizer*)gestureRecognizer {
-  if (self.sharedState.currentlyInEditMode ||
-      gestureRecognizer.state != UIGestureRecognizerStateBegan) {
-    return;
-  }
-  CGPoint touchPoint =
-      [gestureRecognizer locationInView:self.sharedState.tableView];
-  NSIndexPath* indexPath =
-      [self.sharedState.tableView indexPathForRowAtPoint:touchPoint];
-  if (indexPath == nil || [self.sharedState.tableViewModel
-                              sectionIdentifierForSection:indexPath.section] !=
-                              BookmarkHomeSectionIdentifierBookmarks) {
-    return;
-  }
-
-  const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
-  // Disable the long press gesture if it is a permanent node (not an URL or
-  // Folder).
-  if (!node || ![self isUrlOrFolder:node]) {
-    return;
-  }
-
-  [self.delegate bookmarkTableView:self showContextMenuForNode:node];
-}
-
 // Row selection of the tableView will be cleared after reloadData.  This
 // function is used to restore the row selection.  It also updates editNodes in
 // case some selected nodes are removed.
@@ -816,7 +758,9 @@
       strongSelf.sharedState.faviconDownloadCount++;
       IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState)
           ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache(
-              node->url(), minFaviconSizeInPixel, desiredFaviconSizeInPixel,
+              favicon::FaviconServerFetcherParams::CreateForMobile(
+                  node->url(), minFaviconSizeInPixel,
+                  desiredFaviconSizeInPixel),
               /*may_page_url_be_private=*/true, kTrafficAnnotation,
               base::BindBlockArc(faviconLoadedFromServerBlock));
     }
@@ -839,71 +783,6 @@
          node->type() == BookmarkNode::FOLDER;
 }
 
-#pragma mark - Keyboard
-
-- (void)registerForKeyboardNotifications {
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(keyboardWasShown:)
-             name:UIKeyboardDidShowNotification
-           object:nil];
-
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(keyboardWillBeHidden:)
-             name:UIKeyboardWillHideNotification
-           object:nil];
-}
-
-- (void)removeKeyboardObservers {
-  NSNotificationCenter* notificationCenter =
-      [NSNotificationCenter defaultCenter];
-  [notificationCenter removeObserver:self
-                                name:UIKeyboardDidShowNotification
-                              object:nil];
-  [notificationCenter removeObserver:self
-                                name:UIKeyboardWillHideNotification
-                              object:nil];
-}
-
-// Called when the UIKeyboardDidShowNotification is sent
-- (void)keyboardWasShown:(NSNotification*)aNotification {
-  if (![self.delegate isAtTopOfNavigation:self]) {
-    return;
-  }
-  NSDictionary* info = [aNotification userInfo];
-  CGFloat keyboardTop =
-      [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
-  CGFloat tableBottom = CGRectGetMaxY(
-      [self convertRect:self.sharedState.tableView.frame toView:nil]);
-  CGFloat shiftY =
-      tableBottom - keyboardTop + [BookmarkHomeSharedState keyboardSpacingPt];
-
-  if (shiftY >= 0) {
-    UIEdgeInsets previousContentInsets =
-        self.sharedState.tableView.contentInset;
-    // Shift the content inset to prevent the editing content from being hidden
-    // by the keyboard.
-    UIEdgeInsets contentInsets =
-        UIEdgeInsetsMake(previousContentInsets.top, 0.0, shiftY, 0.0);
-    self.sharedState.tableView.contentInset = contentInsets;
-    self.sharedState.tableView.scrollIndicatorInsets = contentInsets;
-  }
-}
-
-// Called when the UIKeyboardWillHideNotification is sent
-- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
-  if (![self.delegate isAtTopOfNavigation:self]) {
-    return;
-  }
-  UIEdgeInsets previousContentInsets = self.sharedState.tableView.contentInset;
-  // Restore the content inset now that the keyboard has been hidden.
-  UIEdgeInsets contentInsets =
-      UIEdgeInsetsMake(previousContentInsets.top, 0, 0, 0);
-  self.sharedState.tableView.contentInset = contentInsets;
-  self.sharedState.tableView.scrollIndicatorInsets = contentInsets;
-}
-
 #pragma mark - SyncedSessionsObserver
 
 - (void)reloadSessions {
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
index bc9d4c9d..3a5750e 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
@@ -93,7 +93,7 @@
   CGFloat yOffset = 0;
   if (IsUIRefreshPhase1Enabled()) {
     if (IsIPadIdiom()) {
-      yOffset = -89.0;
+      yOffset = -104.0;
     } else {
       yOffset = -48.0;
     }
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer.mm
index 772912f1..f3fb6ea5 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer.mm
@@ -24,8 +24,12 @@
 namespace {
 // Returns whether fullscreen should be disabled for |web_state|'s SSL status.
 // This will return true if the visible NavigationItem's SSL has a broken
-// security style or is showing mixed content.
+// security style or is showing mixed content.  If the UI refresh is enabled,
+// fullscreen does not need to be disabled for certificate issues, as the
+// omnibox security indicator is never fully hidden in fullscreen mode.
 bool ShouldDisableFullscreenForWebStateSSL(web::WebState* web_state) {
+  if (IsUIRefreshPhase1Enabled())
+    return false;
   if (!web_state)
     return false;
   web::NavigationManager* manager = web_state->GetNavigationManager();
@@ -125,12 +129,12 @@
 }
 
 void FullscreenWebStateObserver::SetIsLoading(bool loading) {
-  if (IsUIRefreshPhase1Enabled())
-    return;
-
-  if (!!loading_disabler_.get() == loading)
-    return;
-  loading_disabler_ =
-      loading ? std::make_unique<ScopedFullscreenDisabler>(controller_)
-              : nullptr;
+  if (IsUIRefreshPhase1Enabled()) {
+    if (loading)
+      controller_->ResetModel();
+  } else {
+    loading_disabler_ =
+        loading ? std::make_unique<ScopedFullscreenDisabler>(controller_)
+                : nullptr;
+  }
 }
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer_unittest.mm
index b36b85b..062211df 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer_unittest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_web_state_observer_unittest.mm
@@ -110,6 +110,9 @@
 
 // Tests that the model is disabled when the SSL status is broken.
 TEST_F(FullscreenWebStateObserverTest, DisableForBrokenSSL) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kUIRefreshPhase1);
+
   std::unique_ptr<web::NavigationItem> item = web::NavigationItem::Create();
   item->GetSSL().security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
   navigation_manager().SetVisibleItem(item.get());
@@ -120,3 +123,21 @@
   web_state().OnVisibleSecurityStateChanged();
   EXPECT_TRUE(model().enabled());
 }
+
+// Tests that the model remains enabled when the SSL status is broken and the
+// UI refresh flag is enabled.
+TEST_F(FullscreenWebStateObserverTest,
+       DisableForBrokenSSLWithUIRefreshEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kUIRefreshPhase1);
+
+  std::unique_ptr<web::NavigationItem> item = web::NavigationItem::Create();
+  item->GetSSL().security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
+  navigation_manager().SetVisibleItem(item.get());
+  EXPECT_TRUE(model().enabled());
+  web_state().OnVisibleSecurityStateChanged();
+  EXPECT_TRUE(model().enabled());
+  navigation_manager().SetVisibleItem(nullptr);
+  web_state().OnVisibleSecurityStateChanged();
+  EXPECT_TRUE(model().enabled());
+}
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
index ae5b405..3cecfb57 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
@@ -5,8 +5,10 @@
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_page_control.h"
 
 #import <CoreGraphics/CoreGraphics.h>
+#include <algorithm>
 
 #include "base/logging.h"
+#include "base/numerics/ranges.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_constants.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -65,7 +67,8 @@
 
 // Points that the slider overhangs a segment on each side, or 0 if the slider
 // is narrower than a segment.
-const CGFloat kSliderOverhang = MAX((kSliderWidth - kSegmentWidth) / 2.0, 0.0);
+const CGFloat kSliderOverhang =
+    std::max((kSliderWidth - kSegmentWidth) / 2.0, 0.0);
 
 // Width of the separator bars between segments.
 const CGFloat kSeparatorWidth = 1.0;
@@ -75,7 +78,7 @@
 
 // Overall height of the control -- the larger of the slider and segment
 // heights.
-const CGFloat kOverallHeight = MAX(kSliderHeight, kSegmentHeight);
+const CGFloat kOverallHeight = std::max(kSliderHeight, kSegmentHeight);
 // Overall width of the control -- the background width plusand twice
 // the slider overhang.
 const CGFloat kOverallWidth = kBackgroundWidth + 2 * kSliderOverhang;
@@ -191,7 +194,7 @@
 
 - (void)setSliderPosition:(CGFloat)sliderPosition {
   // Clamp |selectionOffset| to (0.0 - 1.0).
-  sliderPosition = MIN(MAX(0.0, sliderPosition), 1.0);
+  sliderPosition = base::ClampToRange<CGFloat>(sliderPosition, 0.0, 1.0);
   CGPoint center = self.sliderView.center;
   center.x = self.sliderOrigin + self.sliderRange * sliderPosition;
   self.sliderView.center = center;
@@ -248,7 +251,7 @@
     // is moving across two segments.
     CGFloat offsetDelta = abs(newPosition - self.sliderPosition);
     NSTimeInterval duration = offsetDelta * kSliderMoveDuration;
-    [UIView animateWithDuration:MIN(duration, kSliderMoveDuration)
+    [UIView animateWithDuration:std::min(duration, kSliderMoveDuration)
                      animations:^{
                        self.sliderPosition = newPosition;
                      }];
diff --git a/ios/chrome/browser/ui/table_view/BUILD.gn b/ios/chrome/browser/ui/table_view/BUILD.gn
index 7ab4b348..67ed7b59 100644
--- a/ios/chrome/browser/ui/table_view/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/BUILD.gn
@@ -5,6 +5,7 @@
 source_set("table_view") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "chrome_table_view_consumer.h",
     "chrome_table_view_controller.h",
     "chrome_table_view_controller.mm",
     "table_view_model.h",
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_consumer.h b/ios/chrome/browser/ui/table_view/chrome_table_view_consumer.h
new file mode 100644
index 0000000..b95b525
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_consumer.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_TABLE_VIEW_CHROME_TABLE_VIEW_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_CHROME_TABLE_VIEW_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+// ChromeTableViewConsumer declares a basic set of methods that allow table view
+// mediators to update their UI. Individual features can extend this protocol to
+// add feature-specific methods.
+@protocol ChromeTableViewConsumer<NSObject>
+
+// Reconfigures the cells corresponding to the given |items| by calling
+// |configureCell:| on each cell.
+- (void)reconfigureCellsForItems:(NSArray*)items;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CHROME_TABLE_VIEW_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_controller.h b/ios/chrome/browser/ui/table_view/chrome_table_view_controller.h
index 98e16dff..6fa9384e 100644
--- a/ios/chrome/browser/ui/table_view/chrome_table_view_controller.h
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_controller.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/material_components/app_bar_presenting.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_consumer.h"
 #import "ios/chrome/browser/ui/table_view/table_view_model.h"
 
 @class ChromeTableViewStyler;
@@ -19,7 +20,8 @@
 };
 
 // Chrome-specific TableViewController.
-@interface ChromeTableViewController : UITableViewController<AppBarPresenting>
+@interface ChromeTableViewController
+    : UITableViewController<AppBarPresenting, ChromeTableViewConsumer>
 
 // The model of this controller.
 @property(nonatomic, readonly, strong)
@@ -45,9 +47,8 @@
 // override this method in order to get a clean tableViewModel.
 - (void)loadModel NS_REQUIRES_SUPER;
 
-// Reconfigures the cells corresponding to the given |items| by calling
-// |configureCell:| on each cell.
-- (void)reconfigureCellsForItems:(NSArray*)items;
+// Methods for reconfiguring and reloading the table view are provided by
+// ChromeTableViewConsumer.
 
 #pragma mark UIScrollViewDelegate
 
diff --git a/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm b/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
index ab1af6a..148d83d 100644
--- a/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
+++ b/ios/chrome/browser/ui/webui/net_export/net_export_ui.mm
@@ -97,12 +97,11 @@
           GetApplicationContext()->GetNetLog()->net_export_file_writer()),
       state_observer_manager_(this),
       weak_ptr_factory_(this) {
-  file_writer_->Initialize(
-      web::WebThread::GetTaskRunnerForThread(web::WebThread::IO));
+  file_writer_->Initialize();
 }
 
 NetExportMessageHandler::~NetExportMessageHandler() {
-  file_writer_->StopNetLog(nullptr, nullptr);
+  file_writer_->StopNetLog(nullptr);
 }
 
 void NetExportMessageHandler::RegisterMessages() {
@@ -156,14 +155,12 @@
   file_writer_->StartNetLog(
       base::FilePath(), capture_mode, max_log_file_size,
       base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
-      GetChannelString(),
-      {GetApplicationContext()->GetSystemURLRequestContext()});
+      GetChannelString(), GetApplicationContext()->GetSystemNetworkContext());
 }
 
 void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  file_writer_->StopNetLog(
-      nullptr, GetApplicationContext()->GetSystemURLRequestContext());
+  file_writer_->StopNetLog(nullptr);
 }
 
 void NetExportMessageHandler::OnSendNetLog(const base::ListValue* list) {
diff --git a/ios/chrome/browser/web/error_page_egtest.mm b/ios/chrome/browser/web/error_page_egtest.mm
index e191fae8..3ff690f 100644
--- a/ios/chrome/browser/web/error_page_egtest.mm
+++ b/ios/chrome/browser/web/error_page_egtest.mm
@@ -93,6 +93,7 @@
 
 // Sucessfully loads the page, goes back, stops the server, goes forward and
 // reloads.
+// TODO(crbug.com/840489): Remove this test.
 - (void)testGoForwardAfterServerIsDownAndReload {
   // First page loads sucessfully.
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
@@ -127,6 +128,7 @@
 
 // Sucessfully loads the page, then loads the URL which fails to load, then
 // sucessfully goes back to the first page.
+// TODO(crbug.com/840489): Remove this test.
 - (void)testGoBackFromErrorPage {
   // First page loads sucessfully.
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
@@ -146,6 +148,7 @@
 }
 
 // Loads the URL which redirects to unresponsive server.
+// TODO(crbug.com/840489): Remove this test.
 - (void)testRedirectToFailingURL {
   // No response leads to ERR_INTERNET_DISCONNECTED error.
   self.serverRespondsWithContent = NO;
@@ -160,6 +163,7 @@
 
 // Loads the page with iframe, and that iframe fails to load. There should be no
 // error page if the main frame has sucessfully loaded.
+// TODO(crbug.com/840489): Remove this test.
 - (void)testErrorPageInIFrame {
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/iframe?echo-query")];
   [ChromeEarlGrey
diff --git a/ios/components/io_thread/ios_io_thread.mm b/ios/components/io_thread/ios_io_thread.mm
index 270c8a2..9109f7b 100644
--- a/ios/components/io_thread/ios_io_thread.mm
+++ b/ios/components/io_thread/ios_io_thread.mm
@@ -320,7 +320,7 @@
   // Add built-in logs
   ct_verifier->AddLogs(ct_logs);
 
-  globals_->ct_policy_enforcer.reset(new net::CTPolicyEnforcer());
+  globals_->ct_policy_enforcer.reset(new net::DefaultCTPolicyEnforcer());
 
   globals_->ssl_config_service = new net::SSLConfigServiceDefaults();
 
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index dfe4f67..cd4d8a1 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -543,6 +543,7 @@
   deps = [
     ":web",
     "//base/test:test_support",
+    "//ios/testing:embedded_test_server_support",
     "//ios/testing:http_server_bundle_data",
     "//ios/testing:ios_test_support",
     "//ios/web:resources_grit",
@@ -570,6 +571,7 @@
     "public/test/http_server_inttest.mm",
     "test/run_all_unittests.cc",
     "url_loader_inttest.mm",
+    "web_state/error_page_inttest.mm",
     "web_state/favicon_callbacks_inttest.mm",
     "web_state/http_auth_inttest.mm",
     "web_state/navigation_and_load_callbacks_inttest.mm",
diff --git a/ios/web/shell/shell_url_request_context_getter.mm b/ios/web/shell/shell_url_request_context_getter.mm
index 9494ae4..868d33a 100644
--- a/ios/web/shell/shell_url_request_context_getter.mm
+++ b/ios/web/shell/shell_url_request_context_getter.mm
@@ -103,7 +103,7 @@
     storage_->set_cert_transparency_verifier(
         base::WrapUnique(new net::MultiLogCTVerifier));
     storage_->set_ct_policy_enforcer(
-        base::WrapUnique(new net::CTPolicyEnforcer));
+        base::WrapUnique(new net::DefaultCTPolicyEnforcer));
     transport_security_persister_ =
         std::make_unique<net::TransportSecurityPersister>(
             url_request_context_->transport_security_state(), base_path_,
diff --git a/ios/web/web_state/error_page_inttest.mm b/ios/web/web_state/error_page_inttest.mm
new file mode 100644
index 0000000..03510160
--- /dev/null
+++ b/ios/web/web_state/error_page_inttest.mm
@@ -0,0 +1,170 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "ios/testing/embedded_test_server_handlers.h"
+#import "ios/testing/wait_util.h"
+#include "ios/web/public/features.h"
+#import "ios/web/public/navigation_manager.h"
+#include "ios/web/public/reload_type.h"
+#import "ios/web/public/test/navigation_test_util.h"
+#import "ios/web/public/test/web_test_with_web_state.h"
+#import "ios/web/public/test/web_view_content_test_util.h"
+#import "ios/web/public/web_client.h"
+#import "ios/web/public/web_state/web_state.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using testing::WaitUntilConditionOrTimeout;
+
+namespace web {
+
+namespace {
+// Overrides PrepareErrorPage to render all important arguments.
+class TestWebClient : public WebClient {
+  void PrepareErrorPage(NSError* error,
+                        bool is_post,
+                        bool is_off_the_record,
+                        NSString** error_html) override {
+    *error_html =
+        [NSString stringWithFormat:@"domain: %@ code: %ld post: %d otr: %d",
+                                   error.domain, static_cast<long>(error.code),
+                                   is_post, is_off_the_record];
+  }
+};
+}  // namespace
+
+// Test fixture for error page testing. Error page simply renders the arguments
+// passed to WebClient::PrepareErrorPage, so the test also acts as integration
+// test for PrepareErrorPage WebClient method.
+class ErrorPageTest : public WebTestWithWebState {
+ protected:
+  ErrorPageTest() : WebTestWithWebState(std::make_unique<TestWebClient>()) {
+    RegisterDefaultHandlers(&server_);
+    server_.RegisterRequestHandler(base::BindRepeating(
+        &net::test_server::HandlePrefixedRequest, "/echo-query",
+        base::BindRepeating(&testing::HandleEchoQueryOrCloseSocket,
+                            base::ConstRef(server_responds_with_content_))));
+    server_.RegisterRequestHandler(
+        base::BindRepeating(&net::test_server::HandlePrefixedRequest, "/iframe",
+                            base::BindRepeating(&testing::HandleIFrame)));
+    scoped_feature_list_.InitAndEnableFeature(features::kWebErrorPages);
+  }
+
+  void SetUp() override {
+    WebTestWithWebState::SetUp();
+    ASSERT_TRUE(server_.Start());
+  }
+
+  net::EmbeddedTestServer server_;
+  bool server_responds_with_content_ = false;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  DISALLOW_COPY_AND_ASSIGN(ErrorPageTest);
+};
+
+// Loads the URL which fails to load, then sucessfully reloads the page.
+TEST_F(ErrorPageTest, ReloadErrorPage) {
+  // No response leads to -1005 error code.
+  server_responds_with_content_ = false;
+  test::LoadUrl(web_state(), server_.GetURL("/echo-query?foo"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(
+      web_state(), "domain: NSURLErrorDomain code: -1005 post: 0 otr: 1"));
+
+  // Reload the page, which should load without errors.
+  server_responds_with_content_ = true;
+  web_state()->GetNavigationManager()->Reload(ReloadType::NORMAL,
+                                              /*check_for_repost=*/false);
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo"));
+}
+
+// Sucessfully loads the page, stops the server and reloads the page.
+TEST_F(ErrorPageTest, ReloadPageAfterServerIsDown) {
+  // Sucessfully load the page.
+  server_responds_with_content_ = true;
+  test::LoadUrl(web_state(), server_.GetURL("/echo-query?foo"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo"));
+
+  // Reload the page, no response leads to -1005 error code.
+  server_responds_with_content_ = false;
+  web_state()->GetNavigationManager()->Reload(ReloadType::NORMAL,
+                                              /*check_for_repost=*/false);
+  ASSERT_TRUE(test::WaitForWebViewContainingText(
+      web_state(), "domain: NSURLErrorDomain code: -1005 post: 0 otr: 1"));
+}
+
+// Sucessfully loads the page, goes back, stops the server, goes forward and
+// reloads.
+TEST_F(ErrorPageTest, GoForwardAfterServerIsDownAndReload) {
+  // First page loads sucessfully.
+  test::LoadUrl(web_state(), server_.GetURL("/echo"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+
+  // Second page loads sucessfully.
+  server_responds_with_content_ = true;
+  test::LoadUrl(web_state(), server_.GetURL("/echo-query?foo"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo"));
+
+  // Go back to the first page.
+  web_state()->GetNavigationManager()->GoBack();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+
+#if TARGET_IPHONE_SIMULATOR
+  // Go forward. The response will be retrieved from the page cache and will not
+  // present the error page. Page cache may not always exist on device (which is
+  // more memory constrained), so this part of the test is simulator-only.
+  server_responds_with_content_ = false;
+  web_state()->GetNavigationManager()->GoForward();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "foo"));
+
+  // Reload bypasses the cache.
+  web_state()->GetNavigationManager()->Reload(ReloadType::NORMAL,
+                                              /*check_for_repost=*/false);
+  ASSERT_TRUE(test::WaitForWebViewContainingText(
+      web_state(), "domain: NSURLErrorDomain code: -1005 post: 0 otr: 1"));
+#endif  // TARGET_IPHONE_SIMULATOR
+}
+
+// Sucessfully loads the page, then loads the URL which fails to load, then
+// sucessfully goes back to the first page.
+TEST_F(ErrorPageTest, GoBackFromErrorPage) {
+  // First page loads sucessfully.
+  test::LoadUrl(web_state(), server_.GetURL("/echo"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+
+  // Second page fails to load.
+  test::LoadUrl(web_state(), server_.GetURL("/close-socket"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(
+      web_state(), "domain: NSURLErrorDomain code: -1005 post: 0 otr: 1"));
+
+  // Going back should sucessfully load the first page.
+  web_state()->GetNavigationManager()->GoBack();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+}
+
+// Loads the URL which redirects to unresponsive server.
+TEST_F(ErrorPageTest, RedirectToFailingURL) {
+  // No response leads to -1005 error code.
+  server_responds_with_content_ = false;
+  test::LoadUrl(web_state(), server_.GetURL("/server-redirect?echo-query"));
+  ASSERT_TRUE(test::WaitForWebViewContainingText(
+      web_state(), "domain: NSURLErrorDomain code: -1005 post: 0 otr: 1"));
+}
+
+// Loads the page with iframe, and that iframe fails to load. There should be no
+// error page if the main frame has sucessfully loaded.
+TEST_F(ErrorPageTest, ErrorPageInIFrame) {
+  test::LoadUrl(web_state(), server_.GetURL("/iframe?echo-query"));
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(testing::kWaitForPageLoadTimeout, ^{
+    return test::IsWebViewContainingCssSelector(web_state(),
+                                                "iframe[src*='echo-query']");
+  }));
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/navigation_and_load_callbacks_inttest.mm b/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
index 715db95f..90520ae 100644
--- a/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
+++ b/ios/web/web_state/navigation_and_load_callbacks_inttest.mm
@@ -663,15 +663,9 @@
   // Reload web page.
   NavigationContext* context = nullptr;
   int32_t nav_id = 0;
-  if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-    EXPECT_CALL(*decider_, ShouldAllowRequest(_, _, /*from_main_frame=*/true))
-        .WillOnce(Return(true));
-    EXPECT_CALL(observer_, DidStartLoading(web_state()));
-  } else {
-    EXPECT_CALL(observer_, DidStartLoading(web_state()));
-    EXPECT_CALL(*decider_, ShouldAllowRequest(_, _, /*from_main_frame=*/true))
-        .WillOnce(Return(true));
-  }
+  EXPECT_CALL(observer_, DidStartLoading(web_state()));
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _, /*from_main_frame=*/true))
+      .WillOnce(Return(true));
   EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
       .WillOnce(
           VerifyReloadStartedContext(web_state(), url, &context, &nav_id));
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index b8026770..ddb6478 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -1946,7 +1946,16 @@
         // New navigation manager can delegate directly to WKWebView to reload
         // for non-app-specific URLs. The necessary navigation states will be
         // updated in WKNavigationDelegate callbacks.
-        [_webView reload];
+        WKNavigation* navigation = [_webView reload];
+        [_navigationStates setState:web::WKNavigationState::REQUESTED
+                      forNavigation:navigation];
+        std::unique_ptr<web::NavigationContextImpl> navigationContext = [self
+            registerLoadRequestForURL:URL
+                             referrer:self.currentNavItemReferrer
+                           transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
+               sameDocumentNavigation:NO];
+        [_navigationStates setContext:std::move(navigationContext)
+                        forNavigation:navigation];
       } else {
         [self loadCurrentURL];
       }
diff --git a/ios/web_view/internal/web_view_url_request_context_getter.mm b/ios/web_view/internal/web_view_url_request_context_getter.mm
index 347023b3..2c365e7 100644
--- a/ios/web_view/internal/web_view_url_request_context_getter.mm
+++ b/ios/web_view/internal/web_view_url_request_context_getter.mm
@@ -17,6 +17,7 @@
 #include "ios/web_view/internal/web_view_network_delegate.h"
 #include "net/base/cache_type.h"
 #include "net/cert/cert_verifier.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/dns/host_resolver.h"
 #include "net/extras/sqlite/sqlite_channel_id_store.h"
@@ -103,7 +104,7 @@
     storage_->set_cert_transparency_verifier(
         base::WrapUnique(new net::MultiLogCTVerifier));
     storage_->set_ct_policy_enforcer(
-        base::WrapUnique(new net::CTPolicyEnforcer));
+        base::WrapUnique(new net::DefaultCTPolicyEnforcer));
     transport_security_persister_ =
         std::make_unique<net::TransportSecurityPersister>(
             url_request_context_->transport_security_state(), base_path_,
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc
index 1b169f1..11787db 100644
--- a/media/audio/audio_input_controller_unittest.cc
+++ b/media/audio/audio_input_controller_unittest.cc
@@ -82,14 +82,14 @@
   MOCK_METHOD0(Close, void());
 };
 
-class MockUserInputMonitor : public UserInputMonitorBase {
+class MockUserInputMonitor : public UserInputMonitor {
  public:
   MockUserInputMonitor() = default;
 
   uint32_t GetKeyPressCount() const override { return 0; }
 
-  MOCK_METHOD0(StartKeyboardMonitoring, void());
-  MOCK_METHOD0(StopKeyboardMonitoring, void());
+  MOCK_METHOD0(EnableKeyPressMonitoring, void());
+  MOCK_METHOD0(DisableKeyPressMonitoring, void());
 };
 
 class MockAudioInputStream : public AudioInputStream {
@@ -185,13 +185,13 @@
       .Times(AtLeast(10))
       .WillRepeatedly(
           CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner()));
-  EXPECT_CALL(user_input_monitor_, StartKeyboardMonitoring());
+  EXPECT_CALL(user_input_monitor_, EnableKeyPressMonitoring());
   controller_->Record();
 
   // Record and wait until ten Write() callbacks are received.
   base::RunLoop().Run();
 
-  EXPECT_CALL(user_input_monitor_, StopKeyboardMonitoring());
+  EXPECT_CALL(user_input_monitor_, DisableKeyPressMonitoring());
   EXPECT_CALL(sync_writer_, Close());
   CloseAudioController();
 }
@@ -212,10 +212,10 @@
   CreateAudioController();
   ASSERT_TRUE(controller_.get());
 
-  EXPECT_CALL(user_input_monitor_, StartKeyboardMonitoring());
+  EXPECT_CALL(user_input_monitor_, EnableKeyPressMonitoring());
   controller_->Record();
 
-  EXPECT_CALL(user_input_monitor_, StopKeyboardMonitoring());
+  EXPECT_CALL(user_input_monitor_, DisableKeyPressMonitoring());
   EXPECT_CALL(sync_writer_, Close());
   CloseAudioController();
 
diff --git a/media/base/user_input_monitor.cc b/media/base/user_input_monitor.cc
index 4528807..5343138 100644
--- a/media/base/user_input_monitor.cc
+++ b/media/base/user_input_monitor.cc
@@ -4,6 +4,8 @@
 
 #include "media/base/user_input_monitor.h"
 
+#include <utility>
+
 #include "base/atomicops.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
@@ -11,26 +13,28 @@
 namespace media {
 
 uint32_t ReadKeyPressMonitorCount(
-    const base::ReadOnlySharedMemoryMapping& shmem_mapping) {
-  if (!shmem_mapping.IsValid())
+    const base::ReadOnlySharedMemoryMapping& readonly_mapping) {
+  if (!readonly_mapping.IsValid())
     return 0;
 
   // No ordering constraints between Load/Store operations, a temporary
   // inconsistent value is fine.
   return base::subtle::NoBarrier_Load(
-      reinterpret_cast<const base::subtle::Atomic32*>(shmem_mapping.memory()));
+      reinterpret_cast<const base::subtle::Atomic32*>(
+          readonly_mapping.memory()));
 }
 
 void WriteKeyPressMonitorCount(
-    const base::WritableSharedMemoryMapping& shmem_mapping,
+    const base::WritableSharedMemoryMapping& writable_mapping,
     uint32_t count) {
-  if (!shmem_mapping.IsValid())
+  if (!writable_mapping.IsValid())
     return;
 
   // No ordering constraints between Load/Store operations, a temporary
   // inconsistent value is fine.
   base::subtle::NoBarrier_Store(
-      reinterpret_cast<base::subtle::Atomic32*>(shmem_mapping.memory()), count);
+      reinterpret_cast<base::subtle::Atomic32*>(writable_mapping.memory()),
+      count);
 }
 
 #ifdef DISABLE_USER_INPUT_MONITOR
@@ -45,26 +49,48 @@
 
 UserInputMonitor::~UserInputMonitor() = default;
 
-UserInputMonitorBase::UserInputMonitorBase() = default;
+UserInputMonitorBase::UserInputMonitorBase() {
+  DETACH_FROM_SEQUENCE(owning_sequence_);
+}
 
 UserInputMonitorBase::~UserInputMonitorBase() {
   DCHECK_EQ(0u, references_);
 }
 
 void UserInputMonitorBase::EnableKeyPressMonitoring() {
-  base::AutoLock auto_lock(lock_);
-  ++references_;
-  if (references_ == 1) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
+  if (++references_ == 1) {
     StartKeyboardMonitoring();
     DVLOG(2) << "Started keyboard monitoring.";
   }
 }
 
+base::ReadOnlySharedMemoryRegion
+UserInputMonitorBase::EnableKeyPressMonitoringWithMapping() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
+  if (++references_ == 1) {
+    base::MappedReadOnlyRegion shmem =
+        base::ReadOnlySharedMemoryRegion::Create(sizeof(uint32_t));
+    if (!shmem.region.IsValid() || !shmem.mapping.IsValid()) {
+      DVLOG(2) << "Error mapping key press count shmem.";
+      return base::ReadOnlySharedMemoryRegion();
+    }
+
+    key_press_count_region_ =
+        base::ReadOnlySharedMemoryRegion(std::move(shmem.region));
+    WriteKeyPressMonitorCount(shmem.mapping, 0u);
+    StartKeyboardMonitoring(std::move(shmem.mapping));
+    DVLOG(2) << "Started keyboard monitoring.";
+  }
+
+  return key_press_count_region_.Duplicate();
+}
+
 void UserInputMonitorBase::DisableKeyPressMonitoring() {
-  base::AutoLock auto_lock(lock_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   DCHECK_NE(references_, 0u);
-  --references_;
-  if (references_ == 0) {
+  if (--references_ == 0) {
+    key_press_count_region_ = base::ReadOnlySharedMemoryRegion();
     StopKeyboardMonitoring();
     DVLOG(2) << "Stopped keyboard monitoring.";
   }
diff --git a/media/base/user_input_monitor.h b/media/base/user_input_monitor.h
index 6559879e..7b774949 100644
--- a/media/base/user_input_monitor.h
+++ b/media/base/user_input_monitor.h
@@ -58,27 +58,32 @@
 };
 
 // Monitors and notifies about keyboard events.
-// Thread safe.
 class MEDIA_EXPORT UserInputMonitorBase : public UserInputMonitor {
  public:
   UserInputMonitorBase();
   ~UserInputMonitorBase() override;
 
-  // A caller must call EnableKeyPressMonitoring and
-  // DisableKeyPressMonitoring in pair.
+  // A caller must call EnableKeyPressMonitoring(WithMapping) and
+  // DisableKeyPressMonitoring in pair on the same sequence.
   void EnableKeyPressMonitoring() override;
   void DisableKeyPressMonitoring() override;
 
+  // Initializes a MappedReadOnlyRegion storing key press count. Returns a
+  // readonly region to the mapping and passes the writable mapping to platform
+  // specific implementation, to update key press count. If monitoring is
+  // already enabled, it only returns a handle to readonly region.
+  base::ReadOnlySharedMemoryRegion EnableKeyPressMonitoringWithMapping();
+
  private:
   virtual void StartKeyboardMonitoring() = 0;
+  virtual void StartKeyboardMonitoring(
+      base::WritableSharedMemoryMapping mapping) = 0;
   virtual void StopKeyboardMonitoring() = 0;
 
-  // Aquired in EnableKeyPressMonitoring()/DisableKeyPressMonitoring(). Together
-  // with |references_| updated under lock, it is used to ensure operation
-  // ordering for start/stop keyboard monitoring, i.e. start is always followed
-  // by stop and start is only called when keyboard monitoring is stopped.
-  base::Lock lock_;
   size_t references_ = 0;
+  base::ReadOnlySharedMemoryRegion key_press_count_region_;
+
+  SEQUENCE_CHECKER(owning_sequence_);
 
   DISALLOW_COPY_AND_ASSIGN(UserInputMonitorBase);
 };
diff --git a/media/base/user_input_monitor_linux.cc b/media/base/user_input_monitor_linux.cc
index 8435739..b52df992 100644
--- a/media/base/user_input_monitor_linux.cc
+++ b/media/base/user_input_monitor_linux.cc
@@ -43,6 +43,7 @@
 
   uint32_t GetKeyPressCount() const;
   void StartMonitor();
+  void StartMonitorWithMapping(base::WritableSharedMemoryMapping mapping);
   void StopMonitor();
 
  private:
@@ -54,6 +55,9 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
+  // Used for sharing key press count value.
+  std::unique_ptr<base::WritableSharedMemoryMapping> key_press_count_mapping_;
+
   //
   // The following members should only be accessed on the IO thread.
   //
@@ -79,6 +83,8 @@
  private:
   // Private UserInputMonitor overrides.
   void StartKeyboardMonitoring() override;
+  void StartKeyboardMonitoring(
+      base::WritableSharedMemoryMapping mapping) override;
   void StopKeyboardMonitoring() override;
 
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
@@ -192,6 +198,13 @@
   OnXEvent();
 }
 
+void UserInputMonitorLinuxCore::StartMonitorWithMapping(
+    base::WritableSharedMemoryMapping mapping) {
+  StartMonitor();
+  key_press_count_mapping_ =
+      std::make_unique<base::WritableSharedMemoryMapping>(std::move(mapping));
+}
+
 void UserInputMonitorLinuxCore::StopMonitor() {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
 
@@ -219,6 +232,8 @@
     x_control_display_ = NULL;
   }
 
+  key_press_count_mapping_.reset();
+
   // Stop observing message loop destruction if no event is being monitored.
   base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this);
 }
@@ -243,6 +258,10 @@
       XkbKeycodeToKeysym(x_control_display_, event->u.u.detail, 0, 0);
   ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
   counter_.OnKeyboardEvent(type, key_code);
+
+  // Update count value in shared memory.
+  if (key_press_count_mapping_)
+    WriteKeyPressMonitorCount(*key_press_count_mapping_, GetKeyPressCount());
 }
 
 // static
@@ -279,6 +298,14 @@
                                 core_->AsWeakPtr()));
 }
 
+void UserInputMonitorLinux::StartKeyboardMonitoring(
+    base::WritableSharedMemoryMapping mapping) {
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&UserInputMonitorLinuxCore::StartMonitorWithMapping,
+                     core_->AsWeakPtr(), std::move(mapping)));
+}
+
 void UserInputMonitorLinux::StopKeyboardMonitoring() {
   io_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&UserInputMonitorLinuxCore::StopMonitor,
diff --git a/media/base/user_input_monitor_mac.cc b/media/base/user_input_monitor_mac.cc
index f45ec6ce..585cb01 100644
--- a/media/base/user_input_monitor_mac.cc
+++ b/media/base/user_input_monitor_mac.cc
@@ -9,10 +9,16 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/timer/timer.h"
 
 namespace media {
 namespace {
 
+// Update key press count in shared memory twice as frequent as
+// AudioInputController::AudioCallback::OnData() callback for WebRTC.
+constexpr base::TimeDelta kUpdateKeyPressCountIntervalMs =
+    base::TimeDelta::FromMilliseconds(5);
+
 class UserInputMonitorMac : public UserInputMonitorBase {
  public:
   UserInputMonitorMac();
@@ -22,8 +28,18 @@
 
  private:
   void StartKeyboardMonitoring() override;
+  void StartKeyboardMonitoring(
+      base::WritableSharedMemoryMapping mapping) override;
   void StopKeyboardMonitoring() override;
 
+  void UpdateKeyPressCountShmem();
+
+  // Used for sharing key press count value.
+  std::unique_ptr<base::WritableSharedMemoryMapping> key_press_count_mapping_;
+
+  // Timer for updating key press count in |key_press_count_mapping_|.
+  base::RepeatingTimer key_press_count_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(UserInputMonitorMac);
 };
 
@@ -40,7 +56,26 @@
 
 void UserInputMonitorMac::StartKeyboardMonitoring() {}
 
-void UserInputMonitorMac::StopKeyboardMonitoring() {}
+void UserInputMonitorMac::StartKeyboardMonitoring(
+    base::WritableSharedMemoryMapping mapping) {
+  key_press_count_mapping_ =
+      std::make_unique<base::WritableSharedMemoryMapping>(std::move(mapping));
+  key_press_count_timer_.Start(FROM_HERE, kUpdateKeyPressCountIntervalMs, this,
+                               &UserInputMonitorMac::UpdateKeyPressCountShmem);
+}
+
+void UserInputMonitorMac::StopKeyboardMonitoring() {
+  if (!key_press_count_mapping_)
+    return;
+
+  key_press_count_timer_.AbandonAndStop();
+  key_press_count_mapping_.reset();
+}
+
+void UserInputMonitorMac::UpdateKeyPressCountShmem() {
+  DCHECK(key_press_count_mapping_);
+  WriteKeyPressMonitorCount(*key_press_count_mapping_, GetKeyPressCount());
+}
 
 }  // namespace
 
diff --git a/media/base/user_input_monitor_unittest.cc b/media/base/user_input_monitor_unittest.cc
index c974b85..dc4ea40 100644
--- a/media/base/user_input_monitor_unittest.cc
+++ b/media/base/user_input_monitor_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_LINUX)
@@ -38,7 +39,35 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST(UserInputMonitorTest, KeyPressMonitorReadWriteCount) {
+TEST(UserInputMonitorTest, CreatePlatformSpecificWithMapping) {
+#if defined(OS_LINUX)
+  base::MessageLoopForIO message_loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);
+#else
+  base::MessageLoopForUI message_loop;
+#endif  // defined(OS_LINUX)
+
+  std::unique_ptr<UserInputMonitor> monitor = UserInputMonitor::Create(
+      message_loop.task_runner(), message_loop.task_runner());
+
+  if (!monitor)
+    return;
+
+  base::ReadOnlySharedMemoryMapping readonly_mapping =
+      static_cast<UserInputMonitorBase*>(monitor.get())
+          ->EnableKeyPressMonitoringWithMapping()
+          .Map();
+  EXPECT_EQ(0u, ReadKeyPressMonitorCount(readonly_mapping));
+  monitor->DisableKeyPressMonitoring();
+
+  monitor.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Check that read only region remains valid after disable.
+  EXPECT_EQ(0u, ReadKeyPressMonitorCount(readonly_mapping));
+}
+
+TEST(UserInputMonitorTest, ReadWriteKeyPressMonitorCount) {
   std::unique_ptr<base::MappedReadOnlyRegion> shmem =
       std::make_unique<base::MappedReadOnlyRegion>(
           base::ReadOnlySharedMemoryRegion::Create(sizeof(uint32_t)));
diff --git a/media/base/user_input_monitor_win.cc b/media/base/user_input_monitor_win.cc
index bc9f954..d42f98a2 100644
--- a/media/base/user_input_monitor_win.cc
+++ b/media/base/user_input_monitor_win.cc
@@ -57,6 +57,7 @@
 
   uint32_t GetKeyPressCount() const;
   void StartMonitor();
+  void StartMonitorWithMapping(base::WritableSharedMemoryMapping mapping);
   void StopMonitor();
 
  private:
@@ -71,6 +72,9 @@
   // Task runner on which |window_| is created.
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 
+  // Used for sharing key press count value.
+  std::unique_ptr<base::WritableSharedMemoryMapping> key_press_count_mapping_;
+
   // These members are only accessed on the UI thread.
   std::unique_ptr<base::win::MessageWindow> window_;
   KeyboardEventCounter counter_;
@@ -90,6 +94,8 @@
  private:
   // Private UserInputMonitor overrides.
   void StartKeyboardMonitoring() override;
+  void StartKeyboardMonitoring(
+      base::WritableSharedMemoryMapping mapping) override;
   void StopKeyboardMonitoring() override;
 
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
@@ -143,6 +149,13 @@
   base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
 }
 
+void UserInputMonitorWinCore::StartMonitorWithMapping(
+    base::WritableSharedMemoryMapping mapping) {
+  StartMonitor();
+  key_press_count_mapping_ =
+      std::make_unique<base::WritableSharedMemoryMapping>(std::move(mapping));
+}
+
 void UserInputMonitorWinCore::StopMonitor() {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
@@ -159,6 +172,8 @@
 
   window_ = nullptr;
 
+  key_press_count_mapping_.reset();
+
   // Stop observing message loop destruction if no event is being monitored.
   base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this);
 }
@@ -196,6 +211,10 @@
     ui::KeyboardCode key_code =
         ui::KeyboardCodeForWindowsKeyCode(input->data.keyboard.VKey);
     counter_.OnKeyboardEvent(event, key_code);
+
+    // Update count value in shared memory.
+    if (key_press_count_mapping_)
+      WriteKeyPressMonitorCount(*key_press_count_mapping_, GetKeyPressCount());
   }
 
   return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
@@ -241,6 +260,14 @@
                                 core_->AsWeakPtr()));
 }
 
+void UserInputMonitorWin::StartKeyboardMonitoring(
+    base::WritableSharedMemoryMapping mapping) {
+  ui_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&UserInputMonitorWinCore::StartMonitorWithMapping,
+                     core_->AsWeakPtr(), std::move(mapping)));
+}
+
 void UserInputMonitorWin::StopKeyboardMonitoring() {
   ui_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&UserInputMonitorWinCore::StopMonitor,
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h
index bcd693e..e4bcbb11 100644
--- a/media/base/video_frame_metadata.h
+++ b/media/base/video_frame_metadata.h
@@ -109,7 +109,7 @@
     // if ALLOW_OVERLAY is set.  However, it allows us to process the overlay
     // to see if it would have been promoted, if it were backed by a SurfaceView
     // instead.  This lets us figure out when SurfaceViews are appropriate.
-    SURFACE_TEXTURE,
+    TEXTURE_OWNER,
 
     // Android only: if set, then this frame's resource would like to be
     // notified about its promotability to an overlay.
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 254366a..8d22210 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -2124,6 +2124,29 @@
   client_->TimeChanged();
 }
 
+void WebMediaPlayerImpl::FlingingStarted() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  DCHECK(!disable_pipeline_auto_suspend_);
+  disable_pipeline_auto_suspend_ = true;
+
+  // Capabilities reporting should only be performed for local playbacks.
+  video_decode_stats_reporter_.reset();
+
+  // Requests to restart media pipeline. A flinging renderer will be created via
+  // the |renderer_factory_selector_|.
+  ScheduleRestart();
+}
+
+void WebMediaPlayerImpl::FlingingStopped() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  DCHECK(disable_pipeline_auto_suspend_);
+  disable_pipeline_auto_suspend_ = false;
+
+  CreateVideoDecodeStatsReporter();
+
+  ScheduleRestart();
+}
+
 void WebMediaPlayerImpl::OnDisconnectedFromRemoteDevice(double t) {
   DoSeek(base::TimeDelta::FromSecondsD(t), false);
 
@@ -3061,6 +3084,7 @@
 void WebMediaPlayerImpl::SwitchToRemoteRenderer(
     const std::string& remote_device_friendly_name) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
+  DCHECK(!disable_pipeline_auto_suspend_);
   disable_pipeline_auto_suspend_ = true;
 
   // Capabilities reporting should only be performed for local playbacks.
@@ -3078,6 +3102,7 @@
 void WebMediaPlayerImpl::SwitchToLocalRenderer(
     MediaObserverClient::ReasonToSwitchToLocal reason) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
+  DCHECK(disable_pipeline_auto_suspend_);
   disable_pipeline_auto_suspend_ = false;
 
   // Capabilities reporting may resume now that playback is local.
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 97fd6bf..87883e7 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -243,10 +243,15 @@
 
   void RequestRemotePlaybackDisabled(bool disabled) override;
 #if defined(OS_ANDROID)  // WMPI_CAST
+  // TODO(https://crbug.com/839651): Rename Flinging[Started/Stopped] to
+  // RemotePlayback[Started/Stopped] once the other RemotePlayback methods have
+  // been removed
   bool IsRemote() const override;
   void RequestRemotePlayback() override;
   void RequestRemotePlaybackControl() override;
   void RequestRemotePlaybackStop() override;
+  void FlingingStarted() override;
+  void FlingingStopped() override;
 
   void SetMediaPlayerManager(
       RendererMediaPlayerManagerInterface* media_player_manager);
diff --git a/media/capture/video/video_capture_system_impl.cc b/media/capture/video/video_capture_system_impl.cc
index 98cbe01..2c41b18 100644
--- a/media/capture/video/video_capture_system_impl.cc
+++ b/media/capture/video/video_capture_system_impl.cc
@@ -143,10 +143,17 @@
   }
 
   devices_info_cache_.swap(new_devices_info_cache);
-  base::ResetAndReturn(&device_enum_request_queue_.front())
-      .Run(devices_info_cache_);
-
+  auto request_cb = std::move(device_enum_request_queue_.front());
   device_enum_request_queue_.pop_front();
+  // If |request_cb| was the last callback in |device_enum_request_queue_|,
+  // |this| may be out of scope after running it. We need to be careful to
+  // not touch the state of |this| after running the callback in this case.
+  if (device_enum_request_queue_.empty()) {
+    std::move(request_cb).Run(devices_info_cache_);
+    return;
+  }
+  std::move(request_cb).Run(devices_info_cache_);
   ProcessDeviceInfoRequest();
 }
+
 }  // namespace media
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
index 953b89d..c251354 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
@@ -4,18 +4,20 @@
 
 #include "media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h"
 
+#include "base/bind_helpers.h"
 #include "base/logging.h"
+#include "media/base/content_decryption_module.h"
 #include "media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h"
 
 namespace media {
 
-ClearKeyCdmProxy::ClearKeyCdmProxy() {}
+ClearKeyCdmProxy::ClearKeyCdmProxy() : weak_factory_(this) {}
 
 ClearKeyCdmProxy::~ClearKeyCdmProxy() {}
 
-// TODO(xhwang): Returns a non-null pointer and add a test covering this path.
 base::WeakPtr<CdmContext> ClearKeyCdmProxy::GetCdmContext() {
-  return nullptr;
+  DVLOG(1) << __func__;
+  return weak_factory_.GetWeakPtr();
 }
 
 void ClearKeyCdmProxy::Initialize(Client* client, InitializeCB init_cb) {
@@ -31,7 +33,7 @@
                                const std::vector<uint8_t>& input_data,
                                uint32_t expected_output_data_size,
                                ProcessCB process_cb) {
-  DVLOG(1) << __func__;
+  DVLOG(2) << __func__;
 
   if (crypto_session_id != kClearKeyCdmProxyCryptoSessionId ||
       !std::equal(input_data.begin(), input_data.end(),
@@ -50,7 +52,7 @@
 void ClearKeyCdmProxy::CreateMediaCryptoSession(
     const std::vector<uint8_t>& input_data,
     CreateMediaCryptoSessionCB create_media_crypto_session_cb) {
-  DVLOG(1) << __func__;
+  DVLOG(2) << __func__;
 
   if (!std::equal(input_data.begin(), input_data.end(),
                   kClearKeyCdmProxyInputData.begin(),
@@ -70,4 +72,16 @@
 void ClearKeyCdmProxy::RemoveKey(uint32_t crypto_session_id,
                                  const std::vector<uint8_t>& key_id) {}
 
+Decryptor* ClearKeyCdmProxy::GetDecryptor() {
+  DVLOG(1) << __func__;
+
+  if (!aes_decryptor_) {
+    aes_decryptor_ = base::MakeRefCounted<AesDecryptor>(
+        base::DoNothing(), base::DoNothing(), base::DoNothing(),
+        base::DoNothing());
+  }
+
+  return aes_decryptor_.get();
+}
+
 }  // namespace media
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
index 5ec686e..20f3d99 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
@@ -7,12 +7,16 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/cdm_context.h"
+#include "media/cdm/aes_decryptor.h"
 #include "media/cdm/cdm_proxy.h"
 
 namespace media {
 
 // CdmProxy implementation for Clear Key CDM to test CDM Proxy support.
-class ClearKeyCdmProxy : public CdmProxy {
+class ClearKeyCdmProxy : public CdmProxy, public CdmContext {
  public:
   ClearKeyCdmProxy();
   ~ClearKeyCdmProxy() final;
@@ -34,7 +38,14 @@
   void RemoveKey(uint32_t crypto_session_id,
                  const std::vector<uint8_t>& key_id) final;
 
+  // CdmContext implementation.
+  Decryptor* GetDecryptor() final;
+
  private:
+  scoped_refptr<AesDecryptor> aes_decryptor_;
+
+  base::WeakPtrFactory<ClearKeyCdmProxy> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ClearKeyCdmProxy);
 };
 
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 297f994..78a29f54 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -95,7 +95,7 @@
   DCHECK(factories_);
 }
 
-void GpuVideoDecoder::Reset(const base::Closure& closure)  {
+void GpuVideoDecoder::Reset(const base::Closure& closure) {
   DVLOG(3) << "Reset()";
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
@@ -121,9 +121,9 @@
                                  const gfx::Size& min_resolution,
                                  const gfx::Size& max_resolution) {
   return (coded_size.width() <= max_resolution.width() &&
-      coded_size.height() <= max_resolution.height() &&
-      coded_size.width() >= min_resolution.width() &&
-      coded_size.height() >= min_resolution.height());
+          coded_size.height() <= max_resolution.height() &&
+          coded_size.width() >= min_resolution.width() &&
+          coded_size.height() >= min_resolution.height());
 }
 
 // Report |success| to UMA and run |cb| with it.  This is super-specific to the
@@ -135,8 +135,8 @@
     bool success) {
   // TODO(xhwang): Report |success| directly.
   PipelineStatus status = success ? PIPELINE_OK : DECODER_ERROR_NOT_SUPPORTED;
-  UMA_HISTOGRAM_ENUMERATION(
-      "Media.GpuVideoDecoderInitializeStatus", status, PIPELINE_STATUS_MAX + 1);
+  UMA_HISTOGRAM_ENUMERATION("Media.GpuVideoDecoderInitializeStatus", status,
+                            PIPELINE_STATUS_MAX + 1);
 
   if (!success) {
     media_log->RecordRapporWithSecurityOrigin(
@@ -466,10 +466,9 @@
 
 void GpuVideoDecoder::RecordBufferData(const BitstreamBuffer& bitstream_buffer,
                                        const DecoderBuffer& buffer) {
-  input_buffer_data_.push_front(BufferData(bitstream_buffer.id(),
-                                           buffer.timestamp(),
-                                           config_.visible_rect(),
-                                           config_.natural_size()));
+  input_buffer_data_.push_front(
+      BufferData(bitstream_buffer.id(), buffer.timestamp(),
+                 config_.visible_rect(), config_.natural_size()));
   // Why this value?  Because why not.  avformat.h:MAX_REORDER_DELAY is 16, but
   // that's too small for some pathological B-frame test videos.  The cost of
   // using too-high a value is low (192 bits per extra slot).
@@ -484,9 +483,8 @@
                                     base::TimeDelta* timestamp,
                                     gfx::Rect* visible_rect,
                                     gfx::Size* natural_size) {
-  for (std::list<BufferData>::const_iterator it =
-           input_buffer_data_.begin(); it != input_buffer_data_.end();
-       ++it) {
+  for (std::list<BufferData>::const_iterator it = input_buffer_data_.begin();
+       it != input_buffer_data_.end(); ++it) {
     if (it->bitstream_buffer_id != id)
       continue;
     *timestamp = it->timestamp;
@@ -520,8 +518,8 @@
                                             uint32_t textures_per_buffer,
                                             const gfx::Size& size,
                                             uint32_t texture_target) {
-  DVLOG(3) << "ProvidePictureBuffers(" << count << ", "
-           << size.width() << "x" << size.height() << ")";
+  DVLOG(3) << "ProvidePictureBuffers(" << count << ", " << size.width() << "x"
+           << size.height() << ")";
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
   std::vector<uint32_t> texture_ids;
@@ -559,8 +557,10 @@
     picture_buffers.push_back(PictureBuffer(next_picture_buffer_id_++, size,
                                             ids, mailboxes, texture_target,
                                             format));
-    bool inserted = assigned_picture_buffers_.insert(std::make_pair(
-        picture_buffers.back().id(), picture_buffers.back())).second;
+    bool inserted = assigned_picture_buffers_
+                        .insert(std::make_pair(picture_buffers.back().id(),
+                                               picture_buffers.back()))
+                        .second;
     DCHECK(inserted);
   }
 
@@ -664,8 +664,8 @@
   frame->set_color_space(picture.color_space());
   if (picture.allow_overlay())
     frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
-  if (picture.surface_texture())
-    frame->metadata()->SetBoolean(VideoFrameMetadata::SURFACE_TEXTURE, true);
+  if (picture.texture_owner())
+    frame->metadata()->SetBoolean(VideoFrameMetadata::TEXTURE_OWNER, true);
   if (picture.wants_promotion_hint()) {
     frame->metadata()->SetBoolean(VideoFrameMetadata::WANTS_PROMOTION_HINT,
                                   true);
@@ -686,8 +686,7 @@
   DeliverFrame(frame);
 }
 
-void GpuVideoDecoder::DeliverFrame(
-    const scoped_refptr<VideoFrame>& frame) {
+void GpuVideoDecoder::DeliverFrame(const scoped_refptr<VideoFrame>& frame) {
   DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
 
   // During a pending vda->Reset(), we don't accumulate frames.  Drop it on the
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 8e03ae1..08bb413c 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -190,8 +190,8 @@
       "android/promotion_hint_aggregator_impl.h",
       "android/surface_chooser_helper.cc",
       "android/surface_chooser_helper.h",
-      "android/surface_texture_gl_owner.cc",
-      "android/surface_texture_gl_owner.h",
+      "android/texture_owner.cc",
+      "android/texture_owner.h",
       "android/texture_pool.cc",
       "android/texture_pool.h",
       "android/texture_wrapper.cc",
@@ -306,6 +306,8 @@
       sources += [
         "windows/d3d11_cdm_proxy.cc",
         "windows/d3d11_cdm_proxy.h",
+        "windows/d3d11_decryptor.cc",
+        "windows/d3d11_decryptor.h",
       ]
     }
   }
@@ -445,8 +447,8 @@
       "android/mock_device_info.h",
       "android/mock_promotion_hint_aggregator.cc",
       "android/mock_promotion_hint_aggregator.h",
-      "android/mock_surface_texture_gl_owner.cc",
-      "android/mock_surface_texture_gl_owner.h",
+      "android/mock_texture_owner.cc",
+      "android/mock_texture_owner.h",
       "android/promotion_hint_aggregator_impl_unittest.cc",
       "android/surface_chooser_helper_unittest.cc",
       "android/surface_texture_gl_owner_unittest.cc",
diff --git a/media/gpu/android/android_video_decode_accelerator.cc b/media/gpu/android/android_video_decode_accelerator.cc
index 0297f1f..bfd44fe8 100644
--- a/media/gpu/android/android_video_decode_accelerator.cc
+++ b/media/gpu/android/android_video_decode_accelerator.cc
@@ -396,7 +396,7 @@
       base::Bind(&AndroidVideoDecodeAccelerator::OnSurfaceTransition,
                  weak_this_factory_.GetWeakPtr(), nullptr));
 
-  // Handle the sync path, which must use SurfaceTexture anyway.  Note that we
+  // Handle the sync path, which must use TextureOwner anyway.  Note that we
   // check both |during_initialize_| and |deferred_initialization_pending_|,
   // since we might get here during deferred surface creation.  In that case,
   // Decode will call us (after clearing |defer_surface_creation_|), but
@@ -414,7 +414,7 @@
     DCHECK(!config_.overlay_info.HasValidSurfaceId());
     DCHECK(!config_.overlay_info.HasValidRoutingToken());
     // Note that we might still send feedback to |surface_chooser_|, which might
-    // call us back.  However, it will only ever tell us to use SurfaceTexture,
+    // call us back.  However, it will only ever tell us to use TextureOwner,
     // since we have no overlay factory anyway.
     OnSurfaceTransition(nullptr);
     return;
@@ -436,7 +436,7 @@
   // Notify |surface_chooser_| that we've started.  This guarantees that we'll
   // get a callback.  It might not be a synchronous callback, but we're not in
   // the synchronous case.  It will be soon, though.  For pre-M, we rely on the
-  // fact that |surface_chooser_| won't tell us to use a SurfaceTexture while
+  // fact that |surface_chooser_| won't tell us to use a TextureOwner while
   // waiting for an overlay to become ready, for example.
   surface_chooser_helper_.UpdateChooserState(std::move(factory));
 }
@@ -466,9 +466,9 @@
   if (!device_info_->IsSetOutputSurfaceSupported())
     return;
 
-  // If we're using a SurfaceTexture and are told to switch to one, then just
+  // If we're using a TextureOwner and are told to switch to one, then just
   // do nothing.  |surface_chooser_| doesn't really know if we've switched to
-  // SurfaceTexture or not.  Note that it can't ask us to switch to the same
+  // TextureOwner or not.  Note that it can't ask us to switch to the same
   // overlay we're using, since it's unique_ptr.
   if (!overlay && codec_config_->surface_bundle &&
       !codec_config_->surface_bundle->overlay) {
@@ -495,10 +495,10 @@
   // incoming bundle properly, since we don't want to accidentally overwrite
   // |surface_bundle| for a codec that's being released elsewhere.
   // TODO(liberato): it doesn't make sense anymore for the PictureBufferManager
-  // to create the surface texture.  We can probably make an overlay impl out
-  // of it, and provide the surface texture to |picture_buffer_manager_|.
+  // to create the texture owner.  We can probably make an overlay impl out
+  // of it, and provide the texture owner to |picture_buffer_manager_|.
   if (!picture_buffer_manager_.Initialize(incoming_bundle_)) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Could not allocate surface texture");
+    NOTIFY_ERROR(PLATFORM_FAILURE, "Could not allocate texture owner");
     incoming_bundle_ = nullptr;
     return;
   }
@@ -927,7 +927,7 @@
   if (want_promotion_hint) {
     picture.set_wants_promotion_hint(true);
     // This will prevent it from actually being promoted if it shouldn't be.
-    picture.set_surface_texture(!allow_overlay);
+    picture.set_texture_owner(!allow_overlay);
   }
 
   // Notify picture ready before calling UseCodecBufferForPictureBuffer() since
@@ -1370,7 +1370,7 @@
   // We cannot get here if we're before surface allocation, since we transition
   // to WAITING_FOR_CODEC (or NO_ERROR, if sync) when we get the surface without
   // posting.  If we do ever lose the surface before starting codec allocation,
-  // then we could just update the config to use a SurfaceTexture and return
+  // then we could just update the config to use a TextureOwner and return
   // without changing state.
   DCHECK_NE(state_, BEFORE_OVERLAY_INIT);
 
@@ -1393,7 +1393,7 @@
   // overlay that was destroyed.
   if (state_ == WAITING_FOR_CODEC) {
     // What we should do here is to set |incoming_overlay_| to nullptr, to start
-    // a transistion to SurfaceTexture.  OnCodecConfigured could notice that
+    // a transistion to TextureOwner.  OnCodecConfigured could notice that
     // there's an incoming overlay, and then immediately transition the codec /
     // drop and re-allocate the codec using it.  However, for CVV, that won't
     // work, since CVV-based overlays block the main thread waiting for the
@@ -1417,16 +1417,16 @@
     picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
 
     // If we aren't transitioning to some other surface, then transition to a
-    // SurfaceTexture.  Remember that, if |incoming_overlay_| is an overlay,
+    // TextureOwner.  Remember that, if |incoming_overlay_| is an overlay,
     // then it's already ready and can be transitioned to immediately.  We were
     // just waiting for codec buffers to come back, but we just dropped them.
     // Note that we want |incoming_overlay_| to has_value(), but that value
-    // should be a nullptr to indicate that we should switch to SurfaceTexture.
+    // should be a nullptr to indicate that we should switch to TextureOwner.
     if (!incoming_overlay_)
       incoming_overlay_ = std::unique_ptr<AndroidOverlay>();
 
     UpdateSurface();
-    // Switching to a SurfaceTexture should never need to wait.  If it does,
+    // Switching to a TextureOwner should never need to wait.  If it does,
     // then the codec might still be using the destroyed surface, which is bad.
     return;
   }
@@ -1513,7 +1513,7 @@
   codec_config_->media_crypto = std::move(media_crypto);
   codec_config_->requires_secure_codec = requires_secure_video_codec;
   // Request a secure surface in all cases.  For L3, it's okay if we fall back
-  // to SurfaceTexture rather than fail composition.  For L1, it's required.
+  // to TextureOwner rather than fail composition.  For L1, it's required.
   // It's also required if the command line says so.
   surface_chooser_helper_.SetSecureSurfaceMode(
       requires_secure_video_codec
@@ -1746,7 +1746,7 @@
     // wouldn't be necessarily true anymore.
     // Also note that we might not have switched surfaces yet, which is also bad
     // for OnSurfaceDestroyed, because of BEFORE_OVERLAY_INIT.  Shouldn't
-    // happen with SurfaceTexture, and OnSurfaceDestroyed checks for it.  In
+    // happen with TextureOwner, and OnSurfaceDestroyed checks for it.  In
     // either case, we definitely should not still have an incoming bundle; it
     // should have been dropped.
     DCHECK(!incoming_bundle_);
diff --git a/media/gpu/android/android_video_decode_accelerator.h b/media/gpu/android/android_video_decode_accelerator.h
index c8e31ab..842a7bd 100644
--- a/media/gpu/android/android_video_decode_accelerator.h
+++ b/media/gpu/android/android_video_decode_accelerator.h
@@ -120,7 +120,7 @@
   // |surface_chooser_| with our initial factory from VDA::Config.
   void StartSurfaceChooser();
 
-  // Start a transition to an overlay, or, if |!overlay|, SurfaceTexture.  The
+  // Start a transition to an overlay, or, if |!overlay|, TextureOwner.  The
   // transition doesn't have to be immediate; we'll favor not dropping frames.
   void OnSurfaceTransition(std::unique_ptr<AndroidOverlay> overlay);
 
@@ -386,7 +386,7 @@
   scoped_refptr<AVDASurfaceBundle> incoming_bundle_;
 
   // If we have been given an overlay to use, then this is it.  If we've been
-  // told to move to SurfaceTexture, then this will be value() == nullptr.
+  // told to move to TextureOwner, then this will be value() == nullptr.
   base::Optional<std::unique_ptr<AndroidOverlay>> incoming_overlay_;
 
   SurfaceChooserHelper surface_chooser_helper_;
@@ -406,7 +406,7 @@
   // Most recently cached frame information, so that we can dispatch it without
   // recomputing it on every frame.  It changes very rarely.
   SurfaceChooserHelper::FrameInformation cached_frame_information_ =
-      SurfaceChooserHelper::FrameInformation::SURFACETEXTURE_INSECURE;
+      SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE;
 
   // WeakPtrFactory for posting tasks back to |this|.
   base::WeakPtrFactory<AndroidVideoDecodeAccelerator> weak_this_factory_;
diff --git a/media/gpu/android/android_video_decode_accelerator_unittest.cc b/media/gpu/android/android_video_decode_accelerator_unittest.cc
index 08eb9e0e..d35a438b 100644
--- a/media/gpu/android/android_video_decode_accelerator_unittest.cc
+++ b/media/gpu/android/android_video_decode_accelerator_unittest.cc
@@ -168,16 +168,16 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void InitializeAVDAWithSurfaceTexture() {
+  void InitializeAVDAWithTextureOwner() {
     ASSERT_TRUE(InitializeAVDA());
     base::RunLoop().RunUntilIdle();
-    // We do not expect a factory, since we are using SurfaceTexture.
+    // We do not expect a factory, since we are using TextureOwner.
     ASSERT_FALSE(chooser_->factory_);
 
     // Set the expectations first, since ProvideOverlay might cause callbacks.
     EXPECT_CALL(*codec_allocator_,
                 MockCreateMediaCodecAsync(nullptr, NotNull()));
-    chooser_->ProvideSurfaceTexture();
+    chooser_->ProvideTextureOwner();
 
     // Provide the codec so that we can check if it's freed properly.
     EXPECT_CALL(client_, NotifyInitializationComplete(true));
@@ -284,7 +284,7 @@
   EXPECT_CALL(client_, NotifyInitializationComplete(false));
 
   ASSERT_TRUE(InitializeAVDA());
-  chooser_->ProvideSurfaceTexture();
+  chooser_->ProvideTextureOwner();
   codec_allocator_->ProvideNullCodecAsync();
 
   // Make sure that codec allocation has happened before destroying the VDA.
@@ -310,22 +310,21 @@
   InitializeAVDA(force_defer_surface_creation);
 }
 
-TEST_F(AndroidVideoDecodeAcceleratorTest,
-       AsyncInitWithSurfaceTextureAndDelete) {
-  // When configuring with a SurfaceTexture and deferred init, we should be
+TEST_F(AndroidVideoDecodeAcceleratorTest, AsyncInitWithTextureOwnerAndDelete) {
+  // When configuring with a TextureOwner and deferred init, we should be
   // asked for a codec, and be notified of init success if we provide one. When
-  // AVDA is destroyed, it should release the codec and surface texture.
+  // AVDA is destroyed, it should release the codec and texture owner.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
-  InitializeAVDAWithSurfaceTexture();
+  InitializeAVDAWithTextureOwner();
 
   // Delete the VDA, and make sure that it tries to free the codec and the right
-  // surface texture.
+  // texture owner.
   EXPECT_CALL(
       *codec_allocator_,
       MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
                             codec_allocator_->most_recent_overlay,
-                            codec_allocator_->most_recent_surface_texture));
+                            codec_allocator_->most_recent_texture_owner));
   codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
@@ -346,21 +345,21 @@
       *codec_allocator_,
       MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
                             codec_allocator_->most_recent_overlay,
-                            codec_allocator_->most_recent_surface_texture));
+                            codec_allocator_->most_recent_texture_owner));
   codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction();
   vda_ = nullptr;
   base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
-       SwitchesToSurfaceTextureWhenSurfaceDestroyed) {
+       SwitchesToTextureOwnerWhenSurfaceDestroyed) {
   // Provide a surface, and a codec, then destroy the surface.  AVDA should use
-  // SetSurface to switch to SurfaceTexture.
+  // SetSurface to switch to TextureOwner.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
   InitializeAVDAWithOverlay();
 
-  // It would be nice if we knew that this was a surface texture.  As it is, we
+  // It would be nice if we knew that this was a texture owner.  As it is, we
   // just destroy the VDA and expect that we're provided with one.  Hopefully,
   // AVDA is actually calling SetSurface properly.
   EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_))
@@ -377,9 +376,9 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(AndroidVideoDecodeAcceleratorTest, SwitchesToSurfaceTextureEventually) {
+TEST_F(AndroidVideoDecodeAcceleratorTest, SwitchesToTextureOwnerEventually) {
   // Provide a surface, and a codec, then request that AVDA switches to a
-  // surface texture.  Verify that it does.
+  // texture owner.  Verify that it does.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
   InitializeAVDAWithOverlay();
@@ -387,12 +386,12 @@
   EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_))
       .WillOnce(Return(true));
 
-  // Note that it's okay if |avda_| switches before ProvideSurfaceTexture
+  // Note that it's okay if |avda_| switches before ProvideTextureOwner
   // returns, since it has no queued output anyway.
-  chooser_->ProvideSurfaceTexture();
+  chooser_->ProvideTextureOwner();
   LetAVDAUpdateSurface();
 
-  // Verify that we're now using some surface texture.
+  // Verify that we're now using some texture owner.
   EXPECT_CALL(*codec_allocator_,
               MockReleaseMediaCodec(codec_allocator_->most_recent_codec,
                                     nullptr, NotNull()));
@@ -404,7 +403,7 @@
 TEST_F(AndroidVideoDecodeAcceleratorTest,
        SetSurfaceFailureDoesntSwitchSurfaces) {
   // Initialize AVDA with a surface, then request that AVDA switches to a
-  // surface texture.  When it tries to UpdateSurface, pretend to fail.  AVDA
+  // texture owner.  When it tries to UpdateSurface, pretend to fail.  AVDA
   // should notify error, and also release the original surface.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
@@ -417,7 +416,7 @@
       .Times(1);
   codec_allocator_->most_recent_codec_destruction_observer
       ->VerifyAndClearExpectations();
-  chooser_->ProvideSurfaceTexture();
+  chooser_->ProvideTextureOwner();
   LetAVDAUpdateSurface();
 }
 
@@ -427,7 +426,7 @@
   // chance to do the first switch.  It should simply drop the overlay.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
-  InitializeAVDAWithSurfaceTexture();
+  InitializeAVDAWithTextureOwner();
 
   // Don't let AVDA switch immediately, else it could choose to SetSurface when
   // it first gets the overlay.
@@ -445,15 +444,15 @@
   // Now it is expected to drop the overlay.
   observer->ExpectDestruction();
 
-  // While the incoming surface is pending, switch back to SurfaceTexture.
-  chooser_->ProvideSurfaceTexture();
+  // While the incoming surface is pending, switch back to TextureOwner.
+  chooser_->ProvideTextureOwner();
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
        ChangingOutputSurfaceVoluntarilyWithoutSetSurfaceIsIgnored) {
-  // If we ask AVDA to change to SurfaceTexture should be ignored on platforms
+  // If we ask AVDA to change to TextureOwner should be ignored on platforms
   // that don't support SetSurface (pre-M or blacklisted).  It should also
-  // ignore SurfaceTexture => overlay, but we don't check that.
+  // ignore TextureOwner => overlay, but we don't check that.
   //
   // Also note that there are other probably reasonable things to do (like
   // signal an error), but we want to be sure that it doesn't try to SetSurface.
@@ -469,8 +468,8 @@
   InitializeAVDAWithOverlay();
   EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
 
-  // This should not switch to SurfaceTexture.
-  chooser_->ProvideSurfaceTexture();
+  // This should not switch to TextureOwner.
+  chooser_->ProvideTextureOwner();
   LetAVDAUpdateSurface();
 }
 
@@ -501,15 +500,15 @@
 }
 
 TEST_F(AndroidVideoDecodeAcceleratorTest,
-       MultipleSurfaceTextureCallbacksAreIgnored) {
+       MultipleTextureOwnerCallbacksAreIgnored) {
   // Ask AVDA to switch to ST when it's already using ST, nothing should happen.
   SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE();
 
-  InitializeAVDAWithSurfaceTexture();
+  InitializeAVDAWithTextureOwner();
 
   // This should do nothing.
   EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0);
-  chooser_->ProvideSurfaceTexture();
+  chooser_->ProvideTextureOwner();
 
   base::RunLoop().RunUntilIdle();
 }
diff --git a/media/gpu/android/android_video_surface_chooser.h b/media/gpu/android/android_video_surface_chooser.h
index dc738138..7789c45 100644
--- a/media/gpu/android/android_video_surface_chooser.h
+++ b/media/gpu/android/android_video_surface_chooser.h
@@ -54,17 +54,16 @@
 
   // Notify the client that the most recently provided overlay should be
   // discarded.  The overlay is still valid, but we recommend against
-  // using it soon, in favor of a SurfaceTexture.
-  using UseSurfaceTextureCB = base::RepeatingCallback<void(void)>;
+  // using it soon, in favor of a TextureOwner.
+  using UseTextureOwnerCB = base::RepeatingCallback<void(void)>;
 
   AndroidVideoSurfaceChooser() {}
   virtual ~AndroidVideoSurfaceChooser() {}
 
   // Sets the client callbacks to be called when a new surface choice is made.
   // Must be called before UpdateState();
-  virtual void SetClientCallbacks(
-      UseOverlayCB use_overlay_cb,
-      UseSurfaceTextureCB use_surface_texture_cb) = 0;
+  virtual void SetClientCallbacks(UseOverlayCB use_overlay_cb,
+                                  UseTextureOwnerCB use_texture_owner_cb) = 0;
 
   // Updates the current state and makes a new surface choice with the new
   // state. If |new_factory| is empty, the factory is left as-is. Otherwise,
diff --git a/media/gpu/android/android_video_surface_chooser_impl.cc b/media/gpu/android/android_video_surface_chooser_impl.cc
index f1faaa4e2..79152879 100644
--- a/media/gpu/android/android_video_surface_chooser_impl.cc
+++ b/media/gpu/android/android_video_surface_chooser_impl.cc
@@ -30,10 +30,10 @@
 
 void AndroidVideoSurfaceChooserImpl::SetClientCallbacks(
     UseOverlayCB use_overlay_cb,
-    UseSurfaceTextureCB use_surface_texture_cb) {
-  DCHECK(use_overlay_cb && use_surface_texture_cb);
+    UseTextureOwnerCB use_texture_owner_cb) {
+  DCHECK(use_overlay_cb && use_texture_owner_cb);
   use_overlay_cb_ = std::move(use_overlay_cb);
-  use_surface_texture_cb_ = std::move(use_surface_texture_cb);
+  use_texture_owner_cb_ = std::move(use_texture_owner_cb);
 }
 
 void AndroidVideoSurfaceChooserImpl::UpdateState(
@@ -53,13 +53,13 @@
       initial_state_received_ = true;
       // Choose here so that Choose() doesn't have to handle non-dynamic.
       // Note that we ignore |is_expecting_relayout| here, since it's transient.
-      // We don't want to pick SurfaceTexture permanently for that.
+      // We don't want to pick TextureOwner permanently for that.
       if (overlay_factory_ &&
           (current_state_.is_fullscreen || current_state_.is_secure ||
            current_state_.is_required)) {
         SwitchToOverlay(false);
       } else {
-        SwitchToSurfaceTexture();
+        SwitchToTextureOwner();
       }
     }
     return;
@@ -87,9 +87,8 @@
   DCHECK(allow_dynamic_);
 
   // TODO(liberato): should this depend on resolution?
-  OverlayState new_overlay_state = current_state_.promote_aggressively
-                                       ? kUsingOverlay
-                                       : kUsingSurfaceTexture;
+  OverlayState new_overlay_state =
+      current_state_.promote_aggressively ? kUsingOverlay : kUsingTextureOwner;
   // Do we require a power-efficient overlay?
   bool needs_power_efficient = current_state_.promote_aggressively;
 
@@ -111,7 +110,7 @@
 
   // If the compositor won't promote, then don't.
   if (!current_state_.is_compositor_promotable)
-    new_overlay_state = kUsingSurfaceTexture;
+    new_overlay_state = kUsingTextureOwner;
 
   // If we're expecting a relayout, then don't transition to overlay if we're
   // not already in one.  We don't want to transition out, though.  This lets us
@@ -119,7 +118,7 @@
   // TODO(liberato): Detect this more directly.
   if (current_state_.is_expecting_relayout &&
       client_overlay_state_ != kUsingOverlay)
-    new_overlay_state = kUsingSurfaceTexture;
+    new_overlay_state = kUsingTextureOwner;
 
   // If we're requesting an overlay, check that we haven't asked too recently
   // since the last failure.  This includes L1.  We don't bother to check for
@@ -129,7 +128,7 @@
     base::TimeDelta time_since_last_failure =
         tick_clock_->NowTicks() - most_recent_overlay_failure_;
     if (time_since_last_failure < MinimumDelayAfterFailedOverlay)
-      new_overlay_state = kUsingSurfaceTexture;
+      new_overlay_state = kUsingTextureOwner;
   }
 
   // If an overlay is required, then choose one.  The only way we won't is if we
@@ -142,16 +141,16 @@
 
   // If we have no factory, then we definitely don't want to use overlays.
   if (!overlay_factory_)
-    new_overlay_state = kUsingSurfaceTexture;
+    new_overlay_state = kUsingTextureOwner;
 
   // Make sure that we're in |new_overlay_state_|.
-  if (new_overlay_state == kUsingSurfaceTexture)
-    SwitchToSurfaceTexture();
+  if (new_overlay_state == kUsingTextureOwner)
+    SwitchToTextureOwner();
   else
     SwitchToOverlay(needs_power_efficient);
 }
 
-void AndroidVideoSurfaceChooserImpl::SwitchToSurfaceTexture() {
+void AndroidVideoSurfaceChooserImpl::SwitchToTextureOwner() {
   // Invalidate any outstanding deletion callbacks for any overlays that we've
   // provided to the client already.  We assume that it will eventually drop
   // them in response to the callback.  Ready / failed callbacks aren't affected
@@ -164,11 +163,11 @@
     overlay_ = nullptr;
 
   // Notify the client to switch if it's in the wrong state.
-  if (client_overlay_state_ != kUsingSurfaceTexture) {
-    DCHECK(use_surface_texture_cb_);
+  if (client_overlay_state_ != kUsingTextureOwner) {
+    DCHECK(use_texture_owner_cb_);
 
-    client_overlay_state_ = kUsingSurfaceTexture;
-    use_surface_texture_cb_.Run();
+    client_overlay_state_ = kUsingTextureOwner;
+    use_texture_owner_cb_.Run();
   }
 }
 
@@ -218,7 +217,7 @@
 
   overlay_ = overlay_factory_.Run(std::move(config));
   if (!overlay_)
-    SwitchToSurfaceTexture();
+    SwitchToTextureOwner();
 }
 
 void AndroidVideoSurfaceChooserImpl::OnOverlayReady(AndroidOverlay* overlay) {
@@ -243,18 +242,18 @@
   overlay_ = nullptr;
   most_recent_overlay_failure_ = tick_clock_->NowTicks();
 
-  // If the client isn't already using a SurfaceTexture, then switch to it.
+  // If the client isn't already using a TextureOwner, then switch to it.
   // Note that this covers the case of kUnknown, when we might not have told the
   // client anything yet.  That's important for Initialize, so that a failed
   // overlay request still results in some callback to the client to know what
   // surface to start with.
-  SwitchToSurfaceTexture();
+  SwitchToTextureOwner();
 }
 
 void AndroidVideoSurfaceChooserImpl::OnOverlayDeleted(AndroidOverlay* overlay) {
-  client_overlay_state_ = kUsingSurfaceTexture;
-  // We don't call SwitchToSurfaceTexture since the client dropped the overlay.
-  // It's already using SurfaceTexture.
+  client_overlay_state_ = kUsingTextureOwner;
+  // We don't call SwitchToTextureOwner since the client dropped the overlay.
+  // It's already using TextureOwner.
 }
 
 void AndroidVideoSurfaceChooserImpl::OnPowerEfficientState(
@@ -264,7 +263,7 @@
   // callback if it arrives.  Getting a new overlay clears any previous cbs.
   DCHECK(!overlay_);
 
-  // We cannot receive it after switching to SurfaceTexture, since that also
+  // We cannot receive it after switching to TextureOwner, since that also
   // clears all callbacks.
   DCHECK(client_overlay_state_ == kUsingOverlay);
 
@@ -285,7 +284,7 @@
   // We don't want to delay transitioning to an overlay if the user re-enters
   // fullscreen.  TODO(liberato): Perhaps we should just clear the failure timer
   // if we detect a transition into fs when we get new state from the client.
-  SwitchToSurfaceTexture();
+  SwitchToTextureOwner();
 }
 
 }  // namespace media
diff --git a/media/gpu/android/android_video_surface_chooser_impl.h b/media/gpu/android/android_video_surface_chooser_impl.h
index e3b1a21..f5a5ecd5 100644
--- a/media/gpu/android/android_video_surface_chooser_impl.h
+++ b/media/gpu/android/android_video_surface_chooser_impl.h
@@ -30,20 +30,20 @@
 
   // AndroidVideoSurfaceChooser
   void SetClientCallbacks(UseOverlayCB use_overlay_cb,
-                          UseSurfaceTextureCB use_surface_texture_cb) override;
+                          UseTextureOwnerCB use_texture_owner_cb) override;
   void UpdateState(base::Optional<AndroidOverlayFactoryCB> new_factory,
                    const State& new_state) override;
 
  private:
-  // Choose whether we should be using a SurfaceTexture or overlay, and issue
+  // Choose whether we should be using a TextureOwner or overlay, and issue
   // the right callbacks if we're changing between them.  This should only be
   // called if |allow_dynamic_|.
   void Choose();
 
-  // Start switching to SurfaceTexture or overlay, as needed.  These will call
+  // Start switching to TextureOwner or overlay, as needed.  These will call
   // the client callbacks if we're changing state, though those callbacks might
   // happen after this returns.
-  void SwitchToSurfaceTexture();
+  void SwitchToTextureOwner();
   // If |overlay_| has an in-flight request, then this will do nothing.  If
   // |power_efficient|, then we will require a power-efficient overlay, and
   // cancel it if it becomes not power efficient.
@@ -57,7 +57,7 @@
 
   // Client callbacks.
   UseOverlayCB use_overlay_cb_;
-  UseSurfaceTextureCB use_surface_texture_cb_;
+  UseTextureOwnerCB use_texture_owner_cb_;
 
   // Current overlay that we've constructed but haven't received ready / failed
   // callbacks yet.  Will be nullptr if we haven't constructed one, or if we
@@ -72,7 +72,7 @@
 
   enum OverlayState {
     kUnknown,
-    kUsingSurfaceTexture,
+    kUsingTextureOwner,
     kUsingOverlay,
   };
 
diff --git a/media/gpu/android/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
index 76e760d..24310a7 100644
--- a/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
+++ b/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
@@ -40,7 +40,7 @@
   }
 
   // Note that this won't clear |overlay_|, which is helpful.
-  MOCK_METHOD0(UseSurfaceTexture, void(void));
+  MOCK_METHOD0(UseTextureOwner, void(void));
 
   // Let the test have the overlay.
   std::unique_ptr<AndroidOverlay> ReleaseOverlay() {
@@ -128,8 +128,10 @@
     chooser_ = std::make_unique<AndroidVideoSurfaceChooserImpl>(allow_dynamic_,
                                                                 &tick_clock_);
     chooser_->SetClientCallbacks(
-        base::Bind(&MockClient::UseOverlayImpl, base::Unretained(&client_)),
-        base::Bind(&MockClient::UseSurfaceTexture, base::Unretained(&client_)));
+        base::BindRepeating(&MockClient::UseOverlayImpl,
+                            base::Unretained(&client_)),
+        base::BindRepeating(&MockClient::UseTextureOwner,
+                            base::Unretained(&client_)));
     chooser_->UpdateState(
         factory ? base::make_optional(std::move(factory)) : base::nullopt,
         chooser_state_);
@@ -207,28 +209,27 @@
 };
 
 TEST_F(AndroidVideoSurfaceChooserImplTest,
-       InitializeWithoutFactoryUsesSurfaceTexture) {
+       InitializeWithoutFactoryUsesTextureOwner) {
   // Calling Initialize() with no factory should result in a callback to use
-  // surface texture.
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  // texture owner.
+  EXPECT_CALL(client_, UseTextureOwner());
   StartChooser(AndroidOverlayFactoryCB());
 }
 
-TEST_F(AndroidVideoSurfaceChooserImplTest,
-       NullInitialOverlayUsesSurfaceTexture) {
+TEST_F(AndroidVideoSurfaceChooserImplTest, NullInitialOverlayUsesTextureOwner) {
   // If we provide a factory, but it fails to create an overlay, then |client_|
-  // should be notified to use a surface texture.
+  // should be notified to use a texture owner.
 
   chooser_state_.is_fullscreen = true;
   EXPECT_CALL(*this, MockOnOverlayCreated());
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   StartChooser(FactoryFor(nullptr));
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest,
-       FailedInitialOverlayUsesSurfaceTexture) {
+       FailedInitialOverlayUsesTextureOwner) {
   // If we provide a factory, but the overlay that it provides returns 'failed',
-  // then |client_| should use surface texture.  Also check that it won't retry
+  // then |client_| should use texture owner.  Also check that it won't retry
   // after a failed overlay too soon.
   chooser_state_.is_fullscreen = true;
   EXPECT_CALL(*this, MockOnOverlayCreated());
@@ -241,7 +242,7 @@
   // doesn't have to be destroyed.  We just care that it hasn't been destroyed
   // before now.
   destruction_observer_ = nullptr;
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   overlay_callbacks_.OverlayFailed.Run();
   testing::Mock::VerifyAndClearExpectations(&client_);
   testing::Mock::VerifyAndClearExpectations(this);
@@ -262,38 +263,38 @@
   testing::Mock::VerifyAndClearExpectations(this);
 }
 
-TEST_F(AndroidVideoSurfaceChooserImplTest, NullLaterOverlayUsesSurfaceTexture) {
+TEST_F(AndroidVideoSurfaceChooserImplTest, NullLaterOverlayUsesTextureOwner) {
   // If an overlay factory is provided after startup that returns a null overlay
   // from CreateOverlay, |chooser_| should, at most, notify |client_| to use
-  // SurfaceTexture zero or more times.
+  // TextureOwner zero or more times.
 
-  // Start with SurfaceTexture.
+  // Start with TextureOwner.
   chooser_state_.is_fullscreen = true;
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   allow_dynamic_ = true;
   StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory that will return a null overlay.
   EXPECT_CALL(*this, MockOnOverlayCreated());
-  EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
+  EXPECT_CALL(client_, UseTextureOwner()).Times(AnyNumber());
   chooser_->UpdateState(FactoryFor(nullptr), chooser_state_);
 }
 
 TEST_F(AndroidVideoSurfaceChooserImplTest, FailedLaterOverlayDoesNothing) {
   // If we send an overlay factory that returns an overlay, and that overlay
   // fails, then the client should not be notified except for zero or more
-  // callbacks to switch to surface texture.
+  // callbacks to switch to texture owner.
 
-  // Start with SurfaceTexture.
+  // Start with TextureOwner.
   chooser_state_.is_fullscreen = true;
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory.
   EXPECT_CALL(*this, MockOnOverlayCreated());
-  EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
+  EXPECT_CALL(client_, UseTextureOwner()).Times(AnyNumber());
   chooser_->UpdateState(FactoryFor(std::move(overlay_)), chooser_state_);
   testing::Mock::VerifyAndClearExpectations(&client_);
 
@@ -307,17 +308,17 @@
        SuccessfulLaterOverlayNotifiesClient) {
   // |client_| is notified if we provide a factory that gets an overlay.
 
-  // Start with SurfaceTexture.
+  // Start with TextureOwner.
   chooser_state_.is_fullscreen = true;
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   StartChooser(AndroidOverlayFactoryCB());
   testing::Mock::VerifyAndClearExpectations(&client_);
 
   // Provide a factory.  |chooser_| should try to create an overlay.  We don't
-  // care if a call to UseSurfaceTexture is elided or not.  Note that AVDA will
-  // ignore duplicate calls anyway (MultipleSurfaceTextureCallbacksAreIgnored).
+  // care if a call to UseTextureOwner is elided or not.  Note that AVDA will
+  // ignore duplicate calls anyway (MultipleTextureOwnerCallbacksAreIgnored).
   EXPECT_CALL(*this, MockOnOverlayCreated());
-  EXPECT_CALL(client_, UseSurfaceTexture()).Times(AnyNumber());
+  EXPECT_CALL(client_, UseTextureOwner()).Times(AnyNumber());
   chooser_->UpdateState(FactoryFor(std::move(overlay_)), chooser_state_);
   testing::Mock::VerifyAndClearExpectations(&client_);
   testing::Mock::VerifyAndClearExpectations(this);
@@ -330,7 +331,7 @@
 TEST_F(AndroidVideoSurfaceChooserImplTest,
        UpdateStateAfterDeleteRetriesOverlay) {
   // Make sure that SurfaceChooser notices that we delete the overlay, and have
-  // switched back to SurfaceTexture mode.
+  // switched back to TextureOwner mode.
 
   chooser_state_.is_fullscreen = true;
   StartChooserAndProvideOverlay();
@@ -341,7 +342,7 @@
 
   // Force chooser to choose again.  We expect that it will retry the overlay,
   // since the delete should have informed it that we've switched back to
-  // SurfaceTexture without a callback from SurfaceChooser.  If it didn't know
+  // TextureOwner without a callback from SurfaceChooser.  If it didn't know
   // this, then it would think that the client is still using an overlay, and
   // take no action.
 
@@ -355,7 +356,7 @@
 TEST_F(AndroidVideoSurfaceChooserImplTest,
        PowerEffcientOverlayCancelsIfNotPowerEfficient) {
   // If we request a power efficient overlay that later becomes not power
-  // efficient, then the client should switch to SurfaceTexture.
+  // efficient, then the client should switch to TextureOwner.
 
   chooser_state_.promote_aggressively = true;
   MockAndroidOverlay* overlay = StartChooserAndProvideOverlay();
@@ -366,14 +367,14 @@
   ASSERT_TRUE(overlay->config()->power_efficient);
 
   // Notify the chooser that it's not power efficient anymore.
-  EXPECT_CALL(client_, UseSurfaceTexture());
+  EXPECT_CALL(client_, UseTextureOwner());
   overlay_callbacks_.PowerEfficientState.Run(false);
 }
 
 TEST_P(AndroidVideoSurfaceChooserImplTest, OverlayIsUsedOrNotBasedOnState) {
   // Provide a factory, and verify that it is used when the state says that it
   // should be.  If the overlay is used, then we also verify that it does not
-  // switch to SurfaceTexture first, since pre-M requires it.
+  // switch to TextureOwner first, since pre-M requires it.
 
   const bool should_use_overlay = IsYes(ShouldUseOverlay, 0);
   const bool should_be_power_efficient = IsYes(ShouldBePowerEfficient, 1);
@@ -389,10 +390,10 @@
   MockAndroidOverlay* overlay = overlay_.get();
 
   if (should_use_overlay) {
-    EXPECT_CALL(client_, UseSurfaceTexture()).Times(0);
+    EXPECT_CALL(client_, UseTextureOwner()).Times(0);
     EXPECT_CALL(*this, MockOnOverlayCreated());
   } else {
-    EXPECT_CALL(client_, UseSurfaceTexture());
+    EXPECT_CALL(client_, UseTextureOwner());
     EXPECT_CALL(*this, MockOnOverlayCreated()).Times(0);
   }
 
@@ -409,8 +410,8 @@
   }
 }
 
-// Unless we're promoting aggressively, we should default to SurfaceTexture.
-INSTANTIATE_TEST_CASE_P(NoFullscreenUsesSurfaceTexture,
+// Unless we're promoting aggressively, we should default to TextureOwner.
+INSTANTIATE_TEST_CASE_P(NoFullscreenUsesTextureOwner,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(ShouldBePowerEfficient::Ignored),
@@ -463,10 +464,10 @@
 
 // For all dynamic cases, we shouldn't use an overlay if the compositor won't
 // promote it, unless it's marked as required.  This includes secure surfaces,
-// so that L3 will fall back to SurfaceTexture.  Non-dynamic is excluded, since
+// so that L3 will fall back to TextureOwner.  Non-dynamic is excluded, since
 // we don't get (or use) compositor feedback before the first frame.  At that
 // point, we've already chosen the output surface and can't switch it.
-INSTANTIATE_TEST_CASE_P(NotCCPromotableNotRequiredUsesSurfaceTexture,
+INSTANTIATE_TEST_CASE_P(NotCCPromotableNotRequiredUsesTextureOwner,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(ShouldBePowerEfficient::No),
@@ -480,7 +481,7 @@
 
 // If we're expecting a relayout, then we should never use an overlay unless
 // it's required.
-INSTANTIATE_TEST_CASE_P(InsecureExpectingRelayoutUsesSurfaceTexture,
+INSTANTIATE_TEST_CASE_P(InsecureExpectingRelayoutUsesTextureOwner,
                         AndroidVideoSurfaceChooserImplTest,
                         Combine(Values(ShouldUseOverlay::No),
                                 Values(ShouldBePowerEfficient::No),
diff --git a/media/gpu/android/avda_codec_image.cc b/media/gpu/android/avda_codec_image.cc
index e1a2b60..66de3d4 100644
--- a/media/gpu/android/avda_codec_image.cc
+++ b/media/gpu/android/avda_codec_image.cc
@@ -23,7 +23,7 @@
     : shared_state_(shared_state),
       codec_buffer_index_(kInvalidCodecBufferIndex),
       media_codec_(codec),
-      has_surface_texture_(false),
+      has_texture_owner_(false),
       texture_(0) {}
 
 AVDACodecImage::~AVDACodecImage() {}
@@ -43,25 +43,25 @@
 void AVDACodecImage::ReleaseTexImage(unsigned target) {}
 
 bool AVDACodecImage::CopyTexImage(unsigned target) {
-  if (!has_surface_texture_ || target != GL_TEXTURE_EXTERNAL_OES)
+  if (!has_texture_owner_ || target != GL_TEXTURE_EXTERNAL_OES)
     return false;
 
   GLint bound_service_id = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
   // We insist that the currently bound texture is the right one.
   if (bound_service_id !=
-      static_cast<GLint>(shared_state_->surface_texture_service_id())) {
+      static_cast<GLint>(shared_state_->texture_owner_service_id())) {
     return false;
   }
 
   // Make sure that we have the right image in the front buffer.  Note that the
-  // bound_service_id is guaranteed to be equal to the surface texture's client
+  // bound_service_id is guaranteed to be equal to the texture owner's client
   // texture id, so we can skip preserving it if the right context is current.
   UpdateSurfaceInternal(UpdateMode::RENDER_TO_FRONT_BUFFER,
                         kDontRestoreBindings);
 
   // By setting image state to UNBOUND instead of COPIED we ensure that
-  // CopyTexImage() is called each time the surface texture is used for drawing.
+  // CopyTexImage() is called each time the texture owner is used for drawing.
   // It would be nice if we could do this via asking for the currently bound
   // Texture, but the active unit never seems to change.
   texture_->SetLevelImageState(GL_TEXTURE_EXTERNAL_OES, 0,
@@ -83,9 +83,9 @@
                                           const gfx::RectF& crop_rect,
                                           bool enable_blend) {
   // This should only be called when we're rendering to a SurfaceView.
-  if (has_surface_texture_) {
+  if (has_texture_owner_) {
     DVLOG(1) << "Invalid call to ScheduleOverlayPlane; this image is "
-                "SurfaceTexture backed.";
+                "TextureOwner backed.";
     return false;
   }
 
@@ -103,8 +103,8 @@
                                   uint64_t process_tracing_id,
                                   const std::string& dump_name) {}
 
-void AVDACodecImage::UpdateSurfaceTexture(RestoreBindingsMode mode) {
-  DCHECK(has_surface_texture_);
+void AVDACodecImage::UpdateTextureOwner(RestoreBindingsMode mode) {
+  DCHECK(has_texture_owner_);
   DCHECK_EQ(codec_buffer_index_, kUpdateOnly);
   codec_buffer_index_ = kRendered;
 
@@ -137,9 +137,9 @@
 }
 
 void AVDACodecImage::SetBufferMetadata(int buffer_index,
-                                       bool has_surface_texture,
+                                       bool has_texture_owner,
                                        const gfx::Size& size) {
-  has_surface_texture_ = has_surface_texture;
+  has_texture_owner_ = has_texture_owner;
   codec_buffer_index_ = buffer_index;
   size_ = size;
 }
@@ -162,7 +162,7 @@
   ReleaseOutputBuffer(update_mode);
 
   // SurfaceViews are updated implicitly, so no further steps are necessary.
-  if (!has_surface_texture_) {
+  if (!has_texture_owner_) {
     DCHECK(update_mode != UpdateMode::RENDER_TO_BACK_BUFFER);
     return;
   }
@@ -171,7 +171,7 @@
   if (update_mode != UpdateMode::RENDER_TO_FRONT_BUFFER)
     return;
 
-  UpdateSurfaceTexture(attached_bindings_mode);
+  UpdateTextureOwner(attached_bindings_mode);
 }
 
 void AVDACodecImage::ReleaseOutputBuffer(UpdateMode update_mode) {
@@ -191,7 +191,7 @@
   DCHECK(update_mode == UpdateMode::RENDER_TO_BACK_BUFFER ||
          update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER);
 
-  if (!has_surface_texture_) {
+  if (!has_texture_owner_) {
     DCHECK(update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER);
     DCHECK_GE(codec_buffer_index_, 0);
     media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true);
@@ -202,14 +202,14 @@
   // If we've already released to the back buffer, there's nothing left to do,
   // but wait for the previously released buffer if necessary.
   if (codec_buffer_index_ != kUpdateOnly) {
-    DCHECK(has_surface_texture_);
+    DCHECK(has_texture_owner_);
     DCHECK_GE(codec_buffer_index_, 0);
-    shared_state_->RenderCodecBufferToSurfaceTexture(media_codec_,
-                                                     codec_buffer_index_);
+    shared_state_->RenderCodecBufferToTextureOwner(media_codec_,
+                                                   codec_buffer_index_);
     codec_buffer_index_ = kUpdateOnly;
   }
 
-  // Only wait for the SurfaceTexture update if we're rendering to the front.
+  // Only wait for the TextureOwner update if we're rendering to the front.
   if (update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER)
     shared_state_->WaitForFrameAvailable();
 }
@@ -227,7 +227,7 @@
 
 void AVDACodecImage::GetTextureMatrix(float matrix[16]) {
   // Our current matrix may be stale.  Update it if possible.
-  if (has_surface_texture_)
+  if (has_texture_owner_)
     UpdateSurface(UpdateMode::RENDER_TO_FRONT_BUFFER);
   shared_state_->GetTransformMatrix(matrix);
   YInvertMatrix(matrix);
diff --git a/media/gpu/android/avda_codec_image.h b/media/gpu/android/avda_codec_image.h
index 78c5978..37071b08 100644
--- a/media/gpu/android/avda_codec_image.h
+++ b/media/gpu/android/avda_codec_image.h
@@ -21,7 +21,7 @@
 
 class MediaCodecBridge;
 
-// GLImage that renders MediaCodec buffers to a SurfaceTexture or SurfaceView as
+// GLImage that renders MediaCodec buffers to a TextureOwner or SurfaceView as
 // needed in order to draw them.
 class AVDACodecImage : public gpu::gles2::GLStreamTextureImage {
  public:
@@ -61,11 +61,11 @@
     DISCARD_CODEC_BUFFER,
 
     // Renders to back buffer, no UpdateTexImage(); can only be used with a
-    // valid |surface_texture_|.
+    // valid |texture_owner_|.
     RENDER_TO_BACK_BUFFER,
 
     // Renders to the back buffer. When used with a SurfaceView, promotion to
-    // the front buffer is automatic. When using a |surface_texture_|,
+    // the front buffer is automatic. When using a |texture_owner_|,
     // UpdateTexImage() is called to promote the back buffer into the front.
     RENDER_TO_FRONT_BUFFER
   };
@@ -81,10 +81,10 @@
   void set_texture(gpu::gles2::Texture* texture) { texture_ = texture; }
 
   // Sets up the properties necessary for the image to render. |buffer_index| is
-  // supplied to ReleaseOutputBuffer(), |has_surface_texture| controls which
+  // supplied to ReleaseOutputBuffer(), |has_texture_owner| controls which
   // rendering path is used, and |size| is used by the compositor.
   void SetBufferMetadata(int buffer_index,
-                         bool has_surface_texture,
+                         bool has_texture_owner,
                          const gfx::Size& size);
 
   bool SetSharedState(scoped_refptr<AVDASharedState> shared_state);
@@ -105,18 +105,18 @@
   ~AVDACodecImage() override;
 
  private:
-  // Make sure that the surface texture's front buffer is current.  This will
+  // Make sure that the texture owner's front buffer is current.  This will
   // save / restore the current context.  It will optionally restore the texture
-  // bindings in the surface texture's context, based on |mode|.  This is
+  // bindings in the texture owner's context, based on |mode|.  This is
   // intended as a hint if we don't need to change contexts.  If we do need to
   // change contexts, then we'll always preserve the texture bindings in the
   // both contexts.  In other words, the caller is telling us whether it's
   // okay to change the binding in the current context.
   enum RestoreBindingsMode { kDontRestoreBindings, kDoRestoreBindings };
-  void UpdateSurfaceTexture(RestoreBindingsMode mode);
+  void UpdateTextureOwner(RestoreBindingsMode mode);
 
   // Internal helper for UpdateSurface() that allows callers to specify the
-  // RestoreBindingsMode when a SurfaceTexture is already attached prior to
+  // RestoreBindingsMode when a TextureOwner is already attached prior to
   // calling this method.
   void UpdateSurfaceInternal(UpdateMode update_mode,
                              RestoreBindingsMode attached_bindings_mode);
@@ -148,9 +148,9 @@
   // May be null.
   MediaCodecBridge* media_codec_;
 
-  // Indicates if we're rendering to a SurfaceTexture or not. Set during the
+  // Indicates if we're rendering to a TextureOwner or not. Set during the
   // call to SetBufferMetadata().
-  bool has_surface_texture_;
+  bool has_texture_owner_;
 
   // The texture that we're attached to.
   gpu::gles2::Texture* texture_;
diff --git a/media/gpu/android/avda_picture_buffer_manager.cc b/media/gpu/android/avda_picture_buffer_manager.cc
index feb47dcc..ee4c4ee 100644
--- a/media/gpu/android/avda_picture_buffer_manager.cc
+++ b/media/gpu/android/avda_picture_buffer_manager.cc
@@ -49,20 +49,19 @@
 bool AVDAPictureBufferManager::Initialize(
     scoped_refptr<AVDASurfaceBundle> surface_bundle) {
   shared_state_ = nullptr;
-  surface_texture_ = nullptr;
+  texture_owner_ = nullptr;
 
   if (!surface_bundle->overlay) {
-    // Create the surface texture.
-    surface_texture_ = SurfaceTextureGLOwnerImpl::Create();
-    if (!surface_texture_)
+    // Create the texture owner.
+    texture_owner_ = SurfaceTextureGLOwner::Create();
+    if (!texture_owner_)
       return false;
 
-    surface_bundle->surface_texture_surface =
-        surface_texture_->CreateJavaSurface();
-    surface_bundle->surface_texture = surface_texture_;
+    surface_bundle->texture_owner_surface = texture_owner_->CreateJavaSurface();
+    surface_bundle->texture_owner_ = texture_owner_;
   }
 
-  // Only do this once the surface texture is filled in, since the constructor
+  // Only do this once the texture owner is filled in, since the constructor
   // assumes that it will be.
   shared_state_ = new AVDASharedState(surface_bundle);
   shared_state_->SetPromotionHintCB(state_provider_->GetPromotionHintCB());
@@ -77,7 +76,7 @@
 
   ReleaseCodecBuffers(buffers);
   CodecChanged(nullptr);
-  surface_texture_ = nullptr;
+  texture_owner_ = nullptr;
 }
 
 void AVDAPictureBufferManager::SetImageForPicture(
@@ -98,8 +97,8 @@
   GLuint stream_texture_service_id = 0;
   if (image) {
     // Override the Texture's service id, so that it will use the one that is
-    // attached to the SurfaceTexture.
-    stream_texture_service_id = shared_state_->surface_texture_service_id();
+    // attached to the TextureOwner
+    stream_texture_service_id = shared_state_->texture_owner_service_id();
 
     // Also set the parameters for the level if we're not clearing the image.
     const gfx::Size size = state_provider_->GetSize();
@@ -110,16 +109,16 @@
     static_cast<AVDACodecImage*>(image)->set_texture(texture_ref->texture());
   }
 
-  // If we're clearing the image, or setting a SurfaceTexture backed image, we
-  // set the state to UNBOUND. For SurfaceTexture images, this ensures that the
+  // If we're clearing the image, or setting a TextureOwner backed image, we
+  // set the state to UNBOUND. For TextureOwner images, this ensures that the
   // implementation will call CopyTexImage, which is where AVDACodecImage
-  // updates the SurfaceTexture to the right frame.
+  // updates the TextureOwner to the right frame.
   auto image_state = gpu::gles2::Texture::UNBOUND;
   // For SurfaceView we set the state to BOUND because ScheduleOverlayPlane
   // requires it. If something tries to sample from this texture it won't work,
   // but there's no way to sample from a SurfaceView anyway, so it doesn't
   // matter.
-  if (image && !surface_texture_)
+  if (image && !texture_owner_)
     image_state = gpu::gles2::Texture::BOUND;
   texture_manager->SetLevelStreamTextureImage(texture_ref, kTextureTarget, 0,
                                               image, image_state,
@@ -144,7 +143,7 @@
   // Note that this is not a race, since we do not re-use a PictureBuffer
   // until after the CC is done drawing it.
   pictures_out_for_display_.push_back(picture_buffer.id());
-  avda_image->SetBufferMetadata(codec_buf_index, !!surface_texture_,
+  avda_image->SetBufferMetadata(codec_buf_index, !!texture_owner_,
                                 state_provider_->GetSize());
 
   // If the shared state has changed for this image, retarget its texture.
@@ -157,7 +156,7 @@
 void AVDAPictureBufferManager::AssignOnePictureBuffer(
     const PictureBuffer& picture_buffer,
     bool have_context) {
-  // Attach a GLImage to each texture that will use the surface texture.
+  // Attach a GLImage to each texture that will use the texture owner.
   scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image =
       codec_images_[picture_buffer.id()] =
           new AVDACodecImage(shared_state_, media_codec_);
@@ -222,10 +221,10 @@
         AVDACodecImage::UpdateMode::RENDER_TO_FRONT_BUFFER);
   }
 
-  // Back buffer rendering is only available for surface textures. We'll always
+  // Back buffer rendering is only available for texture owners. We'll always
   // have at least one front buffer, so the next buffer must be the backbuffer.
   size_t backbuffer_index = front_index + 1;
-  if (!surface_texture_ || backbuffer_index >= pictures_out_for_display_.size())
+  if (!texture_owner_ || backbuffer_index >= pictures_out_for_display_.size())
     return;
 
   // See if the back buffer is free. If so, then render the frame adjacent to
@@ -252,7 +251,7 @@
 bool AVDAPictureBufferManager::ArePicturesOverlayable() {
   // SurfaceView frames are always overlayable because that's the only way to
   // display them.
-  return !surface_texture_;
+  return !texture_owner_;
 }
 
 bool AVDAPictureBufferManager::HasUnrenderedPictures() const {
diff --git a/media/gpu/android/avda_picture_buffer_manager.h b/media/gpu/android/avda_picture_buffer_manager.h
index f2bcc0c..76821f9 100644
--- a/media/gpu/android/avda_picture_buffer_manager.h
+++ b/media/gpu/android/avda_picture_buffer_manager.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "media/gpu/android/avda_state_provider.h"
 #include "media/gpu/android/avda_surface_bundle.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 #include "media/gpu/media_gpu_export.h"
 
 namespace gpu {
@@ -28,7 +28,7 @@
 // AVDAPictureBufferManager is used by AVDA to associate its PictureBuffers with
 // MediaCodec output buffers. It attaches AVDACodecImages to the PictureBuffer
 // textures so that when they're used to draw the AVDACodecImage can release the
-// MediaCodec buffer to the backing Surface. If the Surface is a SurfaceTexture,
+// MediaCodec buffer to the backing Surface. If the Surface is a TextureOwner,
 // the front buffer can then be used to draw without needing to copy the pixels.
 // If the Surface is a SurfaceView, the release causes the frame to be displayed
 // immediately.
@@ -46,7 +46,7 @@
   // (e.g., SurfaceFlinger).  We will ensure that any reference to the bundle
   // is dropped if the overlay sends OnSurfaceDestroyed.
   //
-  // Without an overlay, we will create a SurfaceTexture and add it (and its
+  // Without an overlay, we will create a TextureOwner and add it (and its
   // surface) to |surface_bundle|.  We will arrange to consume the buffers at
   // the right time, in addition to releasing the codec buffers for rendering.
   //
@@ -111,9 +111,9 @@
 
   AVDAStateProvider* const state_provider_;
 
-  // The SurfaceTexture to render to. Non-null after Initialize() if
+  // The texture owner to render to. Non-null after Initialize() if
   // we're not rendering to a SurfaceView.
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+  scoped_refptr<TextureOwner> texture_owner_;
 
   MediaCodecBridge* media_codec_;
 
diff --git a/media/gpu/android/avda_shared_state.cc b/media/gpu/android/avda_shared_state.cc
index dbb035370..fa667246 100644
--- a/media/gpu/android/avda_shared_state.cc
+++ b/media/gpu/android/avda_shared_state.cc
@@ -33,23 +33,22 @@
 
 AVDASharedState::~AVDASharedState() = default;
 
-void AVDASharedState::RenderCodecBufferToSurfaceTexture(
-    MediaCodecBridge* codec,
-    int codec_buffer_index) {
-  if (surface_texture()->IsExpectingFrameAvailable())
-    surface_texture()->WaitForFrameAvailable();
+void AVDASharedState::RenderCodecBufferToTextureOwner(MediaCodecBridge* codec,
+                                                      int codec_buffer_index) {
+  if (texture_owner()->IsExpectingFrameAvailable())
+    texture_owner()->WaitForFrameAvailable();
   codec->ReleaseOutputBuffer(codec_buffer_index, true);
-  surface_texture()->SetReleaseTimeToNow();
+  texture_owner()->SetReleaseTimeToNow();
 }
 
 void AVDASharedState::WaitForFrameAvailable() {
-  surface_texture()->WaitForFrameAvailable();
+  texture_owner()->WaitForFrameAvailable();
 }
 
 void AVDASharedState::UpdateTexImage() {
-  surface_texture()->UpdateTexImage();
+  texture_owner()->UpdateTexImage();
   // Helpfully, this is already column major.
-  surface_texture()->GetTransformMatrix(gl_matrix_);
+  texture_owner()->GetTransformMatrix(gl_matrix_);
 }
 
 void AVDASharedState::GetTransformMatrix(float matrix[16]) const {
@@ -57,8 +56,8 @@
 }
 
 void AVDASharedState::ClearReleaseTime() {
-  if (surface_texture())
-    surface_texture()->IgnorePendingRelease();
+  if (texture_owner())
+    texture_owner()->IgnorePendingRelease();
 }
 
 void AVDASharedState::ClearOverlay(AndroidOverlay* overlay_raw) {
diff --git a/media/gpu/android/avda_shared_state.h b/media/gpu/android/avda_shared_state.h
index 3110eb0..954f775 100644
--- a/media/gpu/android/avda_shared_state.h
+++ b/media/gpu/android/avda_shared_state.h
@@ -29,59 +29,59 @@
  public:
   AVDASharedState(scoped_refptr<AVDASurfaceBundle> surface_bundle);
 
-  GLuint surface_texture_service_id() const {
-    return surface_texture() ? surface_texture()->GetTextureId() : 0;
+  GLuint texture_owner_service_id() const {
+    return texture_owner() ? texture_owner()->GetTextureId() : 0;
   }
 
-  SurfaceTextureGLOwner* surface_texture() const {
-    return surface_bundle_ ? surface_bundle_->surface_texture.get() : nullptr;
+  TextureOwner* texture_owner() const {
+    return surface_bundle_ ? surface_bundle_->texture_owner_.get() : nullptr;
   }
 
   AndroidOverlay* overlay() const {
     return surface_bundle_ ? surface_bundle_->overlay.get() : nullptr;
   }
 
-  // Context and surface that |surface_texture_| is bound to, if
-  // |surface_texture_| is not null.
+  // Context and surface that |texture_owner_| is bound to, if
+  // |texture_owner_| is not null.
   gl::GLContext* context() const {
-    return surface_texture() ? surface_texture()->GetContext() : nullptr;
+    return texture_owner() ? texture_owner()->GetContext() : nullptr;
   }
 
   gl::GLSurface* surface() const {
-    return surface_texture() ? surface_texture()->GetSurface() : nullptr;
+    return texture_owner() ? texture_owner()->GetSurface() : nullptr;
   }
 
   // Helper method for coordinating the interactions between
   // MediaCodec::ReleaseOutputBuffer() and WaitForFrameAvailable() when
-  // rendering to a SurfaceTexture; this method should never be called when
+  // rendering to a TextureOwner; this method should never be called when
   // rendering to a SurfaceView.
   //
-  // The release of the codec buffer to the surface texture is asynchronous, by
+  // The release of the codec buffer to the texture owner is asynchronous, by
   // using this helper we can attempt to let this process complete in a non
-  // blocking fashion before the SurfaceTexture is used.
+  // blocking fashion before the TextureOwner is used.
   //
   // Clients should call this method to release the codec buffer for rendering
-  // and then call WaitForFrameAvailable() before using the SurfaceTexture. In
-  // the ideal case the SurfaceTexture has already been updated, otherwise the
+  // and then call WaitForFrameAvailable() before using the TextureOwner. In
+  // the ideal case the TextureOwner has already been updated, otherwise the
   // method will wait for a pro-rated amount of time based on elapsed time up
   // to a short deadline.
   //
   // Some devices do not reliably notify frame availability, so we use a very
   // short deadline of only a few milliseconds to avoid indefinite stalls.
-  void RenderCodecBufferToSurfaceTexture(MediaCodecBridge* codec,
-                                         int codec_buffer_index);
+  void RenderCodecBufferToTextureOwner(MediaCodecBridge* codec,
+                                       int codec_buffer_index);
 
   void WaitForFrameAvailable();
 
-  // Helper methods for interacting with |surface_texture_|. See
-  // gl::SurfaceTexture for method details.
+  // Helper methods for interacting with |texture_owner_|. See
+  // gl::TextureOwner for method details.
   void UpdateTexImage();
 
   // Returns a matrix that needs to be y flipped in order to match the
   // StreamTextureMatrix contract. See GLStreamTextureImage::YInvertMatrix().
   void GetTransformMatrix(float matrix[16]) const;
 
-  // Resets the last time for RenderCodecBufferToSurfaceTexture(). Should be
+  // Resets the last time for RenderCodecBufferToTextureOwner(). Should be
   // called during codec changes.
   void ClearReleaseTime();
 
@@ -96,7 +96,7 @@
  private:
   friend class base::RefCounted<AVDASharedState>;
 
-  // Texture matrix of the front buffer of the surface texture.
+  // Texture matrix of the front buffer of the texture owner.
   float gl_matrix_[16];
 
   scoped_refptr<AVDASurfaceBundle> surface_bundle_;
diff --git a/media/gpu/android/avda_surface_bundle.cc b/media/gpu/android/avda_surface_bundle.cc
index 1c09e8fcd..38c1320 100644
--- a/media/gpu/android/avda_surface_bundle.cc
+++ b/media/gpu/android/avda_surface_bundle.cc
@@ -20,28 +20,27 @@
       overlay(std::move(overlay)),
       weak_factory_(this) {}
 
-AVDASurfaceBundle::AVDASurfaceBundle(
-    scoped_refptr<SurfaceTextureGLOwner> surface_texture_owner)
+AVDASurfaceBundle::AVDASurfaceBundle(scoped_refptr<TextureOwner> texture_owner)
     : RefCountedDeleteOnSequence<AVDASurfaceBundle>(
           base::SequencedTaskRunnerHandle::Get()),
-      surface_texture(std::move(surface_texture_owner)),
-      surface_texture_surface(surface_texture->CreateJavaSurface()),
+      texture_owner_(std::move(texture_owner)),
+      texture_owner_surface(texture_owner_->CreateJavaSurface()),
       weak_factory_(this) {}
 
 AVDASurfaceBundle::~AVDASurfaceBundle() {
   // Explicitly free the surface first, just to be sure that it's deleted before
-  // the SurfaceTexture is.
-  surface_texture_surface = gl::ScopedJavaSurface();
+  // the TextureOwner is.
+  texture_owner_surface = gl::ScopedJavaSurface();
 
   // Also release the back buffers.
-  if (surface_texture) {
-    auto task_runner = surface_texture->task_runner();
+  if (texture_owner_) {
+    auto task_runner = texture_owner_->task_runner();
     if (task_runner->RunsTasksInCurrentSequence()) {
-      surface_texture->ReleaseBackBuffers();
+      texture_owner_->ReleaseBackBuffers();
     } else {
       task_runner->PostTask(
-          FROM_HERE, base::Bind(&SurfaceTextureGLOwner::ReleaseBackBuffers,
-                                surface_texture));
+          FROM_HERE, base::BindRepeating(&TextureOwner::ReleaseBackBuffers,
+                                         texture_owner_));
     }
   }
 }
@@ -51,7 +50,7 @@
   if (overlay)
     return overlay->GetJavaSurface();
   else
-    return surface_texture_surface.j_surface();
+    return texture_owner_surface.j_surface();
 }
 
 AVDASurfaceBundle::ScheduleLayoutCB AVDASurfaceBundle::GetScheduleLayoutCB() {
diff --git a/media/gpu/android/avda_surface_bundle.h b/media/gpu/android/avda_surface_bundle.h
index e0dc749..7e08b2a 100644
--- a/media/gpu/android/avda_surface_bundle.h
+++ b/media/gpu/android/avda_surface_bundle.h
@@ -8,13 +8,13 @@
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "media/base/android/android_overlay.h"
 #include "media/base/surface_manager.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 #include "media/gpu/media_gpu_export.h"
 #include "ui/gl/android/scoped_java_surface.h"
 
 namespace media {
 
-// AVDASurfaceBundle is a Java surface, and the SurfaceTexture or Overlay that
+// AVDASurfaceBundle is a Java surface, and the TextureOwner or Overlay that
 // backs it.
 //
 // Once a MediaCodec is configured with an output surface, the corresponding
@@ -29,8 +29,7 @@
   // Create an empty bundle to be manually populated.
   explicit AVDASurfaceBundle();
   explicit AVDASurfaceBundle(std::unique_ptr<AndroidOverlay> overlay);
-  explicit AVDASurfaceBundle(
-      scoped_refptr<SurfaceTextureGLOwner> surface_texture_owner);
+  explicit AVDASurfaceBundle(scoped_refptr<TextureOwner> texture_owner);
 
   const base::android::JavaRef<jobject>& GetJavaSurface() const;
 
@@ -39,12 +38,12 @@
   // |this|; the cb will do nothing if |this| is destroyed.
   ScheduleLayoutCB GetScheduleLayoutCB();
 
-  // The Overlay or SurfaceTexture.
+  // The Overlay or TextureOwner.
   std::unique_ptr<AndroidOverlay> overlay;
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture;
+  scoped_refptr<TextureOwner> texture_owner_;
 
-  // The Java surface for |surface_texture|.
-  gl::ScopedJavaSurface surface_texture_surface;
+  // The Java surface for |texture_owner_|.
+  gl::ScopedJavaSurface texture_owner_surface;
 
  private:
   ~AVDASurfaceBundle();
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 4e9450c1..face8a2 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -10,35 +10,34 @@
 
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/texture_manager.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/scoped_make_current.h"
 
 namespace media {
 namespace {
 
-// Makes |surface_texture|'s context current if it isn't already.
+// Makes |texture_owner|'s context current if it isn't already.
 std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
-    SurfaceTextureGLOwner* surface_texture) {
+    TextureOwner* texture_owner) {
   // Note: this works for virtual contexts too, because IsCurrent() returns true
   // if their shared platform context is current, regardless of which virtual
   // context is current.
   return std::unique_ptr<ui::ScopedMakeCurrent>(
-      surface_texture->GetContext()->IsCurrent(nullptr)
+      texture_owner->GetContext()->IsCurrent(nullptr)
           ? nullptr
-          : new ui::ScopedMakeCurrent(surface_texture->GetContext(),
-                                      surface_texture->GetSurface()));
+          : new ui::ScopedMakeCurrent(texture_owner->GetContext(),
+                                      texture_owner->GetSurface()));
 }
 
 }  // namespace
 
 CodecImage::CodecImage(
     std::unique_ptr<CodecOutputBuffer> output_buffer,
-    scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+    scoped_refptr<TextureOwner> texture_owner,
     PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb)
     : phase_(Phase::kInCodec),
       output_buffer_(std::move(output_buffer)),
-      surface_texture_(std::move(surface_texture)),
+      texture_owner_(std::move(texture_owner)),
       promotion_hint_cb_(std::move(promotion_hint_cb)) {}
 
 CodecImage::~CodecImage() {
@@ -68,16 +67,16 @@
 void CodecImage::ReleaseTexImage(unsigned target) {}
 
 bool CodecImage::CopyTexImage(unsigned target) {
-  if (!surface_texture_ || target != GL_TEXTURE_EXTERNAL_OES)
+  if (!texture_owner_ || target != GL_TEXTURE_EXTERNAL_OES)
     return false;
 
   GLint bound_service_id = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-  // The currently bound texture should be the surface texture's texture.
-  if (bound_service_id != static_cast<GLint>(surface_texture_->GetTextureId()))
+  // The currently bound texture should be the texture owner's texture.
+  if (bound_service_id != static_cast<GLint>(texture_owner_->GetTextureId()))
     return false;
 
-  RenderToSurfaceTextureFrontBuffer(BindingsMode::kDontRestore);
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestore);
   return true;
 }
 
@@ -93,9 +92,9 @@
                                       const gfx::Rect& bounds_rect,
                                       const gfx::RectF& crop_rect,
                                       bool enable_blend) {
-  if (surface_texture_) {
+  if (texture_owner_) {
     DVLOG(1) << "Invalid call to ScheduleOverlayPlane; this image is "
-                "SurfaceTexture backed.";
+                "TextureOwner backed.";
     return false;
   }
 
@@ -127,14 +126,14 @@
       0, 1,  0, 1   //
   };
   memcpy(matrix, kYInvertedIdentity, sizeof(kYInvertedIdentity));
-  if (!surface_texture_)
+  if (!texture_owner_)
     return;
 
   // The matrix is available after we render to the front buffer. If that fails
   // we'll return the matrix from the previous frame, which is more likely to be
   // correct than the identity matrix anyway.
-  RenderToSurfaceTextureFrontBuffer(BindingsMode::kDontRestore);
-  surface_texture_->GetTransformMatrix(matrix);
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestore);
+  texture_owner_->GetTransformMatrix(matrix);
   YInvertMatrix(matrix);
 }
 
@@ -145,7 +144,7 @@
                                      int display_height) {
   // If this is promotable, and we're using an overlay, then skip sending this
   // hint.  ScheduleOverlayPlane will do it.
-  if (promotion_hint && !surface_texture_)
+  if (promotion_hint && !texture_owner_)
     return;
 
   promotion_hint_cb_.Run(PromotionHintAggregator::Hint(
@@ -154,13 +153,13 @@
 }
 
 bool CodecImage::RenderToFrontBuffer() {
-  return surface_texture_
-             ? RenderToSurfaceTextureFrontBuffer(BindingsMode::kRestore)
+  return texture_owner_
+             ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestore)
              : RenderToOverlay();
 }
 
-bool CodecImage::RenderToSurfaceTextureBackBuffer() {
-  DCHECK(surface_texture_);
+bool CodecImage::RenderToTextureOwnerBackBuffer() {
+  DCHECK(texture_owner_);
   DCHECK_NE(phase_, Phase::kInFrontBuffer);
   if (phase_ == Phase::kInBackBuffer)
     return true;
@@ -169,35 +168,35 @@
 
   // Wait for a previous frame available so we don't confuse it with the one
   // we're about to release.
-  if (surface_texture_->IsExpectingFrameAvailable())
-    surface_texture_->WaitForFrameAvailable();
+  if (texture_owner_->IsExpectingFrameAvailable())
+    texture_owner_->WaitForFrameAvailable();
   if (!output_buffer_->ReleaseToSurface()) {
     phase_ = Phase::kInvalidated;
     return false;
   }
   phase_ = Phase::kInBackBuffer;
-  surface_texture_->SetReleaseTimeToNow();
+  texture_owner_->SetReleaseTimeToNow();
   return true;
 }
 
-bool CodecImage::RenderToSurfaceTextureFrontBuffer(BindingsMode bindings_mode) {
-  DCHECK(surface_texture_);
+bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
+  DCHECK(texture_owner_);
   if (phase_ == Phase::kInFrontBuffer)
     return true;
   if (phase_ == Phase::kInvalidated)
     return false;
 
   // Render it to the back buffer if it's not already there.
-  if (!RenderToSurfaceTextureBackBuffer())
+  if (!RenderToTextureOwnerBackBuffer())
     return false;
 
   // The image is now in the back buffer, so promote it to the front buffer.
   phase_ = Phase::kInFrontBuffer;
-  if (surface_texture_->IsExpectingFrameAvailable())
-    surface_texture_->WaitForFrameAvailable();
+  if (texture_owner_->IsExpectingFrameAvailable())
+    texture_owner_->WaitForFrameAvailable();
 
   std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
-      MakeCurrentIfNeeded(surface_texture_.get());
+      MakeCurrentIfNeeded(texture_owner_.get());
   // If we have to switch contexts, then we always want to restore the
   // bindings.
   bool should_restore_bindings =
@@ -206,7 +205,7 @@
   GLint bound_service_id = 0;
   if (should_restore_bindings)
     glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-  surface_texture_->UpdateTexImage();
+  texture_owner_->UpdateTexImage();
   if (should_restore_bindings)
     glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
   return true;
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index 19eb6c4b..df4a12a 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -14,12 +14,12 @@
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
 #include "media/gpu/android/codec_wrapper.h"
 #include "media/gpu/android/promotion_hint_aggregator.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 #include "media/gpu/media_gpu_export.h"
 
 namespace media {
 
-// A GLImage that renders MediaCodec buffers to a SurfaceTexture or overlay
+// A GLImage that renders MediaCodec buffers to a TextureOwner or overlay
 // as needed in order to draw them.
 class MEDIA_GPU_EXPORT CodecImage : public gpu::gles2::GLStreamTextureImage {
  public:
@@ -28,7 +28,7 @@
   using DestructionCb = base::RepeatingCallback<void(CodecImage*)>;
 
   CodecImage(std::unique_ptr<CodecOutputBuffer> output_buffer,
-             scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+             scoped_refptr<TextureOwner> texture_owner,
              PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb);
 
   void SetDestructionCb(DestructionCb destruction_cb);
@@ -66,12 +66,10 @@
     return phase_ == Phase::kInFrontBuffer;
   }
 
-  // Whether this image is backed by a surface texture.
-  bool is_surface_texture_backed() const { return !!surface_texture_; }
+  // Whether this image is backed by a texture owner.
+  bool is_texture_owner_backed() const { return !!texture_owner_; }
 
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture() const {
-    return surface_texture_;
-  }
+  scoped_refptr<TextureOwner> texture_owner() const { return texture_owner_; }
 
   // Renders this image to the front buffer of its backing surface.
   // Returns true if the buffer is in the front buffer. Returns false if the
@@ -79,10 +77,10 @@
   // possible to render it.
   bool RenderToFrontBuffer();
 
-  // Renders this image to the back buffer of its surface texture. Only valid if
-  // is_surface_texture_backed(). Returns true if the buffer is in the back
+  // Renders this image to the back buffer of its texture owner. Only valid if
+  // is_texture_owner_backed(). Returns true if the buffer is in the back
   // buffer. Returns false if the buffer was invalidated.
-  bool RenderToSurfaceTextureBackBuffer();
+  bool RenderToTextureOwnerBackBuffer();
 
   // Called when we're no longer renderable because our surface is gone.  We'll
   // discard any codec buffer, and generally do nothing.
@@ -97,16 +95,16 @@
   // kInFrontBuffer and kInvalidated are terminal.
   enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
 
-  // Renders this image to the surface texture front buffer by first rendering
+  // Renders this image to the texture owner front buffer by first rendering
   // it to the back buffer if it's not already there, and then waiting for the
   // frame available event before calling UpdateTexImage(). Passing
   // BindingsMode::kDontRestore skips the work of restoring the current texture
-  // bindings if the surface texture's context is already current. Otherwise,
+  // bindings if the texture owner's context is already current. Otherwise,
   // this switches contexts and preserves the texture bindings.
   // Returns true if the buffer is in the front buffer. Returns false if the
   // buffer was invalidated.
   enum class BindingsMode { kRestore, kDontRestore };
-  bool RenderToSurfaceTextureFrontBuffer(BindingsMode bindings_mode);
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
 
   // Renders this image to the overlay. Returns true if the buffer is in the
   // overlay front buffer. Returns false if the buffer was invalidated.
@@ -118,9 +116,9 @@
   // The buffer backing this image.
   std::unique_ptr<CodecOutputBuffer> output_buffer_;
 
-  // The SurfaceTexture that |output_buffer_| will be rendered to. Or null, if
+  // The TextureOwner that |output_buffer_| will be rendered to. Or null, if
   // this image is backed by an overlay.
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+  scoped_refptr<TextureOwner> texture_owner_;
 
   // The bounds last sent to the overlay.
   gfx::Rect most_recent_bounds_;
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 702e5d7..ccae01c 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -13,7 +13,7 @@
 #include "media/base/android/media_codec_bridge.h"
 #include "media/base/android/mock_media_codec_bridge.h"
 #include "media/gpu/android/codec_image.h"
-#include "media/gpu/android/mock_surface_texture_gl_owner.h"
+#include "media/gpu/android/mock_texture_owner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
@@ -59,8 +59,8 @@
     glGenTextures(1, &texture_id);
     // The tests rely on this texture being bound.
     glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
-    surface_texture_ = new NiceMock<MockSurfaceTextureGLOwner>(
-        texture_id, context_.get(), surface_.get());
+    texture_owner_ = new NiceMock<MockTextureOwner>(texture_id, context_.get(),
+                                                    surface_.get());
   }
 
   void TearDown() override {
@@ -71,14 +71,14 @@
     wrapper_->TakeCodecSurfacePair();
   }
 
-  enum ImageKind { kOverlay, kSurfaceTexture };
+  enum ImageKind { kOverlay, kTextureOwner };
   scoped_refptr<CodecImage> NewImage(
       ImageKind kind,
       CodecImage::DestructionCb destruction_cb = base::DoNothing()) {
     std::unique_ptr<CodecOutputBuffer> buffer;
     wrapper_->DequeueOutputBuffer(nullptr, nullptr, &buffer);
     scoped_refptr<CodecImage> image = new CodecImage(
-        std::move(buffer), kind == kSurfaceTexture ? surface_texture_ : nullptr,
+        std::move(buffer), kind == kTextureOwner ? texture_owner_ : nullptr,
         base::BindRepeating(&PromotionHintReceiver::OnPromotionHint,
                             base::Unretained(&promotion_hint_receiver_)));
 
@@ -89,7 +89,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   NiceMock<MockMediaCodecBridge>* codec_;
   std::unique_ptr<CodecWrapper> wrapper_;
-  scoped_refptr<NiceMock<MockSurfaceTextureGLOwner>> surface_texture_;
+  scoped_refptr<NiceMock<MockTextureOwner>> texture_owner_;
   scoped_refptr<gl::GLContext> context_;
   scoped_refptr<gl::GLShareGroup> share_group_;
   scoped_refptr<gl::GLSurface> surface_;
@@ -110,7 +110,7 @@
 }
 
 TEST_F(CodecImageTest, ImageStartsUnrendered) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   ASSERT_FALSE(i->was_rendered_to_front_buffer());
 }
 
@@ -119,20 +119,20 @@
   ASSERT_FALSE(i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES));
 }
 
-TEST_F(CodecImageTest, ScheduleOverlayPlaneIsInvalidForSurfaceTextureImages) {
-  auto i = NewImage(kSurfaceTexture);
+TEST_F(CodecImageTest, ScheduleOverlayPlaneIsInvalidForTextureOwnerImages) {
+  auto i = NewImage(kTextureOwner);
   ASSERT_FALSE(i->ScheduleOverlayPlane(gfx::AcceleratedWidget(), 0,
                                        gfx::OverlayTransform(), gfx::Rect(),
                                        gfx::RectF(), true));
 }
 
 TEST_F(CodecImageTest, CopyTexImageFailsIfTargetIsNotOES) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   ASSERT_FALSE(i->CopyTexImage(GL_TEXTURE_2D));
 }
 
 TEST_F(CodecImageTest, CopyTexImageFailsIfTheWrongTextureIsBound) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   GLuint wrong_texture_id;
   glGenTextures(1, &wrong_texture_id);
   glBindTexture(GL_TEXTURE_EXTERNAL_OES, wrong_texture_id);
@@ -140,29 +140,29 @@
 }
 
 TEST_F(CodecImageTest, CopyTexImageCanBeCalledRepeatedly) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   ASSERT_TRUE(i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES));
   ASSERT_TRUE(i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES));
 }
 
 TEST_F(CodecImageTest, CopyTexImageTriggersFrontBufferRendering) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   // Verify that the release comes before the wait.
   InSequence s;
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
-  EXPECT_CALL(*surface_texture_, WaitForFrameAvailable());
-  EXPECT_CALL(*surface_texture_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, WaitForFrameAvailable());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage());
   i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES);
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
 }
 
 TEST_F(CodecImageTest, GetTextureMatrixTriggersFrontBufferRendering) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   InSequence s;
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
-  EXPECT_CALL(*surface_texture_, WaitForFrameAvailable());
-  EXPECT_CALL(*surface_texture_, UpdateTexImage());
-  EXPECT_CALL(*surface_texture_, GetTransformMatrix(_));
+  EXPECT_CALL(*texture_owner_, WaitForFrameAvailable());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, GetTransformMatrix(_));
   float matrix[16];
   i->GetTextureMatrix(matrix);
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
@@ -189,39 +189,39 @@
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
 }
 
-TEST_F(CodecImageTest, CanRenderSurfaceTextureImageToBackBuffer) {
-  auto i = NewImage(kSurfaceTexture);
-  ASSERT_TRUE(i->RenderToSurfaceTextureBackBuffer());
+TEST_F(CodecImageTest, CanRenderTextureOwnerImageToBackBuffer) {
+  auto i = NewImage(kTextureOwner);
+  ASSERT_TRUE(i->RenderToTextureOwnerBackBuffer());
   ASSERT_FALSE(i->was_rendered_to_front_buffer());
 }
 
 TEST_F(CodecImageTest, CodecBufferInvalidationResultsInRenderingFailure) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   // Invalidate the backing codec buffer.
   wrapper_->TakeCodecSurfacePair();
-  ASSERT_FALSE(i->RenderToSurfaceTextureBackBuffer());
+  ASSERT_FALSE(i->RenderToTextureOwnerBackBuffer());
 }
 
 TEST_F(CodecImageTest, RenderToBackBufferDoesntWait) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   InSequence s;
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
-  EXPECT_CALL(*surface_texture_, SetReleaseTimeToNow());
-  EXPECT_CALL(*surface_texture_, WaitForFrameAvailable()).Times(0);
-  ASSERT_TRUE(i->RenderToSurfaceTextureBackBuffer());
+  EXPECT_CALL(*texture_owner_, SetReleaseTimeToNow());
+  EXPECT_CALL(*texture_owner_, WaitForFrameAvailable()).Times(0);
+  ASSERT_TRUE(i->RenderToTextureOwnerBackBuffer());
 }
 
 TEST_F(CodecImageTest, PromotingTheBackBufferWaits) {
-  auto i = NewImage(kSurfaceTexture);
-  EXPECT_CALL(*surface_texture_, SetReleaseTimeToNow()).Times(1);
-  i->RenderToSurfaceTextureBackBuffer();
-  EXPECT_CALL(*surface_texture_, WaitForFrameAvailable());
+  auto i = NewImage(kTextureOwner);
+  EXPECT_CALL(*texture_owner_, SetReleaseTimeToNow()).Times(1);
+  i->RenderToTextureOwnerBackBuffer();
+  EXPECT_CALL(*texture_owner_, WaitForFrameAvailable());
   ASSERT_TRUE(i->RenderToFrontBuffer());
 }
 
 TEST_F(CodecImageTest, PromotingTheBackBufferAlwaysSucceeds) {
-  auto i = NewImage(kSurfaceTexture);
-  i->RenderToSurfaceTextureBackBuffer();
+  auto i = NewImage(kTextureOwner);
+  i->RenderToTextureOwnerBackBuffer();
   // Invalidating the codec buffer doesn't matter after it's rendered to the
   // back buffer.
   wrapper_->TakeCodecSurfacePair();
@@ -229,9 +229,9 @@
 }
 
 TEST_F(CodecImageTest, FrontBufferRenderingFailsIfBackBufferRenderingFailed) {
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   wrapper_->TakeCodecSurfacePair();
-  i->RenderToSurfaceTextureBackBuffer();
+  i->RenderToTextureOwnerBackBuffer();
   ASSERT_FALSE(i->RenderToFrontBuffer());
 }
 
@@ -239,8 +239,8 @@
   GLuint pre_bound_texture = 0;
   glGenTextures(1, &pre_bound_texture);
   glBindTexture(GL_TEXTURE_EXTERNAL_OES, pre_bound_texture);
-  auto i = NewImage(kSurfaceTexture);
-  EXPECT_CALL(*surface_texture_, UpdateTexImage());
+  auto i = NewImage(kTextureOwner);
+  EXPECT_CALL(*texture_owner_, UpdateTexImage());
   i->RenderToFrontBuffer();
   GLint post_bound_texture = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &post_bound_texture);
@@ -257,9 +257,9 @@
   context->Initialize(surface.get(), gl::GLContextAttribs());
   ASSERT_TRUE(context->MakeCurrent(surface.get()));
 
-  auto i = NewImage(kSurfaceTexture);
+  auto i = NewImage(kTextureOwner);
   // Our context should not be current when UpdateTexImage() is called.
-  EXPECT_CALL(*surface_texture_, UpdateTexImage()).WillOnce(Invoke([&]() {
+  EXPECT_CALL(*texture_owner_, UpdateTexImage()).WillOnce(Invoke([&]() {
     ASSERT_FALSE(context->IsCurrent(surface.get()));
   }));
   i->RenderToFrontBuffer();
diff --git a/media/gpu/android/fake_codec_allocator.cc b/media/gpu/android/fake_codec_allocator.cc
index 1c9f5d5..e7b80630 100644
--- a/media/gpu/android/fake_codec_allocator.cc
+++ b/media/gpu/android/fake_codec_allocator.cc
@@ -30,7 +30,7 @@
 std::unique_ptr<MediaCodecBridge> FakeCodecAllocator::CreateMediaCodecSync(
     scoped_refptr<CodecConfig> config) {
   CopyCodecConfig(config);
-  MockCreateMediaCodecSync(most_recent_overlay, most_recent_surface_texture);
+  MockCreateMediaCodecSync(most_recent_overlay, most_recent_texture_owner);
 
   std::unique_ptr<MockMediaCodecBridge> codec;
   if (allow_sync_creation) {
@@ -57,14 +57,14 @@
   client_ = client;
   codec_creation_pending_ = true;
 
-  MockCreateMediaCodecAsync(most_recent_overlay, most_recent_surface_texture);
+  MockCreateMediaCodecAsync(most_recent_overlay, most_recent_texture_owner);
 }
 
 void FakeCodecAllocator::ReleaseMediaCodec(
     std::unique_ptr<MediaCodecBridge> media_codec,
     scoped_refptr<AVDASurfaceBundle> surface_bundle) {
   MockReleaseMediaCodec(media_codec.get(), surface_bundle->overlay.get(),
-                        surface_bundle->surface_texture.get());
+                        surface_bundle->texture_owner_.get());
 }
 
 MockMediaCodecBridge* FakeCodecAllocator::ProvideMockCodecAsync(
@@ -97,7 +97,7 @@
 void FakeCodecAllocator::CopyCodecConfig(scoped_refptr<CodecConfig> config) {
   // CodecConfig isn't copyable, since it has unique_ptrs and such.
   most_recent_overlay = config->surface_bundle->overlay.get();
-  most_recent_surface_texture = config->surface_bundle->surface_texture.get();
+  most_recent_texture_owner = config->surface_bundle->texture_owner_.get();
   most_recent_config->media_crypto =
       config->media_crypto
           ? std::make_unique<base::android::ScopedJavaGlobalRef<jobject>>(
diff --git a/media/gpu/android/fake_codec_allocator.h b/media/gpu/android/fake_codec_allocator.h
index e0ff1dd..5823a06 100644
--- a/media/gpu/android/fake_codec_allocator.h
+++ b/media/gpu/android/fake_codec_allocator.h
@@ -30,18 +30,14 @@
   // These are called with some parameters of the codec config by our
   // implementation of their respective functions.  This allows tests to set
   // expectations on them.
-  MOCK_METHOD2(MockCreateMediaCodecSync,
-               void(AndroidOverlay*, SurfaceTextureGLOwner*));
-  MOCK_METHOD2(MockCreateMediaCodecAsync,
-               void(AndroidOverlay*, SurfaceTextureGLOwner*));
+  MOCK_METHOD2(MockCreateMediaCodecSync, void(AndroidOverlay*, TextureOwner*));
+  MOCK_METHOD2(MockCreateMediaCodecAsync, void(AndroidOverlay*, TextureOwner*));
 
   // Note that this doesn't exactly match the signature, since unique_ptr
   // doesn't work.  plus, we expand |surface_bundle| a bit to make it more
   // convenient to set expectations.
   MOCK_METHOD3(MockReleaseMediaCodec,
-               void(MediaCodecBridge*,
-                    AndroidOverlay*,
-                    SurfaceTextureGLOwner*));
+               void(MediaCodecBridge*, AndroidOverlay*, TextureOwner*));
 
   std::unique_ptr<MediaCodecBridge> CreateMediaCodecSync(
       scoped_refptr<CodecConfig> config) override;
@@ -70,8 +66,8 @@
   // The most recent overlay provided during codec allocation.
   AndroidOverlay* most_recent_overlay = nullptr;
 
-  // The most recent surface texture provided during codec allocation.
-  SurfaceTextureGLOwner* most_recent_surface_texture = nullptr;
+  // The most recent texture owner provided during codec allocation.
+  TextureOwner* most_recent_texture_owner = nullptr;
 
   // Whether CreateMediaCodecSync() is allowed to succeed.
   bool allow_sync_creation = true;
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index ae312c0d..70e58ff 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -253,7 +253,7 @@
   requires_secure_codec_ = requires_secure_video_codec;
 
   // Request a secure surface in all cases.  For L3, it's okay if we fall back
-  // to SurfaceTexture rather than fail composition.  For L1, it's required.
+  // to TextureOwner rather than fail composition.  For L1, it's required.
   surface_chooser_helper_.SetSecureSurfaceMode(
       requires_secure_video_codec
           ? SurfaceChooserHelper::SecureSurfaceMode::kRequired
@@ -286,13 +286,13 @@
 }
 
 void MediaCodecVideoDecoder::OnVideoFrameFactoryInitialized(
-    scoped_refptr<SurfaceTextureGLOwner> surface_texture) {
+    scoped_refptr<TextureOwner> texture_owner) {
   DVLOG(2) << __func__;
-  if (!surface_texture) {
+  if (!texture_owner) {
     EnterTerminalState(State::kError);
     return;
   }
-  surface_texture_bundle_ = new AVDASurfaceBundle(std::move(surface_texture));
+  texture_owner_bundle_ = new AVDASurfaceBundle(std::move(texture_owner));
 
   // Overlays are disabled when |enable_threaded_texture_mailboxes| is true
   // (http://crbug.com/582170).
@@ -338,7 +338,7 @@
                    weak_factory_.GetWeakPtr()));
     target_surface_bundle_ = new AVDASurfaceBundle(std::move(overlay));
   } else {
-    target_surface_bundle_ = surface_texture_bundle_;
+    target_surface_bundle_ = texture_owner_bundle_;
   }
 
   // If we were waiting for our first surface during initialization, then
@@ -367,7 +367,7 @@
   // Reset the target bundle if it is the one being destroyed.
   if (target_surface_bundle_ &&
       target_surface_bundle_->overlay.get() == overlay) {
-    target_surface_bundle_ = surface_texture_bundle_;
+    target_surface_bundle_ = texture_owner_bundle_;
   }
 
   // Transition the codec away from the overlay if necessary.
@@ -747,7 +747,7 @@
   pump_codec_timer_.Stop();
   ReleaseCodec();
   target_surface_bundle_ = nullptr;
-  surface_texture_bundle_ = nullptr;
+  texture_owner_bundle_ = nullptr;
   if (state == State::kError)
     CancelPendingDecodes(DecodeStatus::DECODE_ERROR);
   if (drain_type_)
diff --git a/media/gpu/android/media_codec_video_decoder.h b/media/gpu/android/media_codec_video_decoder.h
index adbfa1de..1bdaab2 100644
--- a/media/gpu/android/media_codec_video_decoder.h
+++ b/media/gpu/android/media_codec_video_decoder.h
@@ -44,7 +44,7 @@
 // first Decode() (see StartLazyInit()). We do this because there are cases in
 // our media pipeline where we'll initialize a decoder but never use it
 // (e.g., MSE with no media data appended), and if we eagerly allocator decoder
-// resources, like MediaCodecs and SurfaceTextures, we will block other
+// resources, like MediaCodecs and TextureOwners, we will block other
 // playbacks that need them.
 // TODO: Lazy initialization should be handled at a higher layer of the media
 // stack for both simplicity and cross platform support.
@@ -120,7 +120,7 @@
   // Finishes initialization.
   void StartLazyInit();
   void OnVideoFrameFactoryInitialized(
-      scoped_refptr<SurfaceTextureGLOwner> surface_texture);
+      scoped_refptr<TextureOwner> texture_owner);
 
   // Resets |waiting_for_key_| to false, indicating that MediaCodec might now
   // accept buffers.
@@ -242,9 +242,9 @@
   // non-null from the first surface choice.
   scoped_refptr<AVDASurfaceBundle> target_surface_bundle_;
 
-  // A SurfaceTexture bundle that is kept for the lifetime of MCVD so that if we
+  // A TextureOwner bundle that is kept for the lifetime of MCVD so that if we
   // have to synchronously switch surfaces we always have one available.
-  scoped_refptr<AVDASurfaceBundle> surface_texture_bundle_;
+  scoped_refptr<AVDASurfaceBundle> texture_owner_bundle_;
 
   // A callback for requesting overlay info updates.
   RequestOverlayInfoCB request_overlay_info_cb_;
@@ -267,7 +267,7 @@
   // Most recently cached frame information, so that we can dispatch it without
   // recomputing it on every frame.  It changes very rarely.
   SurfaceChooserHelper::FrameInformation cached_frame_information_ =
-      SurfaceChooserHelper::FrameInformation::SURFACETEXTURE_INSECURE;
+      SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE;
 
   // CDM related stuff.
 
diff --git a/media/gpu/android/media_codec_video_decoder_unittest.cc b/media/gpu/android/media_codec_video_decoder_unittest.cc
index 3032532..38bc0b88 100644
--- a/media/gpu/android/media_codec_video_decoder_unittest.cc
+++ b/media/gpu/android/media_codec_video_decoder_unittest.cc
@@ -21,7 +21,7 @@
 #include "media/gpu/android/fake_codec_allocator.h"
 #include "media/gpu/android/mock_android_video_surface_chooser.h"
 #include "media/gpu/android/mock_device_info.h"
-#include "media/gpu/android/mock_surface_texture_gl_owner.h"
+#include "media/gpu/android/mock_texture_owner.h"
 #include "media/gpu/android/video_frame_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -61,7 +61,7 @@
   MOCK_METHOD6(
       MockCreateVideoFrame,
       void(CodecOutputBuffer* raw_output_buffer,
-           scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+           scoped_refptr<TextureOwner> texture_owner,
            base::TimeDelta timestamp,
            gfx::Size natural_size,
            PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
@@ -74,10 +74,10 @@
       scoped_refptr<AVDASurfaceBundle> surface_bundle) override {
     MockSetSurfaceBundle(surface_bundle);
     if (!surface_bundle) {
-      surface_texture_ = nullptr;
+      texture_owner_ = nullptr;
     } else {
-      surface_texture_ =
-          surface_bundle->overlay ? nullptr : surface_bundle->surface_texture;
+      texture_owner_ =
+          surface_bundle->overlay ? nullptr : surface_bundle->texture_owner_;
     }
   }
 
@@ -87,7 +87,7 @@
       gfx::Size natural_size,
       PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
       VideoDecoder::OutputCB output_cb) override {
-    MockCreateVideoFrame(output_buffer.get(), surface_texture_, timestamp,
+    MockCreateVideoFrame(output_buffer.get(), texture_owner_, timestamp,
                          natural_size, promotion_hint_cb, output_cb);
     last_output_buffer_ = std::move(output_buffer);
     output_cb.Run(VideoFrame::CreateBlackFrame(gfx::Size(10, 10)));
@@ -99,7 +99,7 @@
   }
 
   std::unique_ptr<CodecOutputBuffer> last_output_buffer_;
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+  scoped_refptr<TextureOwner> texture_owner_;
   base::OnceClosure last_closure_;
 };
 
@@ -126,19 +126,18 @@
         std::make_unique<NiceMock<MockAndroidVideoSurfaceChooser>>();
     surface_chooser_ = surface_chooser.get();
 
-    auto surface_texture =
-        base::MakeRefCounted<NiceMock<MockSurfaceTextureGLOwner>>(0, nullptr,
-                                                                  nullptr);
-    surface_texture_ = surface_texture.get();
+    auto texture_owner =
+        base::MakeRefCounted<NiceMock<MockTextureOwner>>(0, nullptr, nullptr);
+    texture_owner_ = texture_owner.get();
 
     auto video_frame_factory =
         std::make_unique<NiceMock<MockVideoFrameFactory>>();
     video_frame_factory_ = video_frame_factory.get();
-    // Set up VFF to pass |surface_texture_| via its InitCb.
+    // Set up VFF to pass |texture_owner_| via its InitCb.
     const bool want_promotion_hint =
         device_info_->IsSetOutputSurfaceSupported();
     ON_CALL(*video_frame_factory_, Initialize(want_promotion_hint, _))
-        .WillByDefault(RunCallback<1>(surface_texture));
+        .WillByDefault(RunCallback<1>(texture_owner));
 
     auto* observable_mcvd = new DestructionObservableMCVD(
         gpu_preferences_, device_info_.get(), codec_allocator_.get(),
@@ -209,19 +208,19 @@
 
   // Call Initialize() and Decode() to start lazy init. MCVD will be waiting for
   // a codec and have one decode pending.
-  void InitializeWithSurfaceTexture_OneDecodePending(
+  void InitializeWithTextureOwner_OneDecodePending(
       VideoDecoderConfig config = TestVideoConfig::Large(kCodecH264)) {
     Initialize(config);
     mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
     provide_overlay_info_cb_.Run(OverlayInfo());
-    surface_chooser_->ProvideSurfaceTexture();
+    surface_chooser_->ProvideTextureOwner();
   }
 
   // Fully initializes MCVD and returns the codec it's configured with. MCVD
   // will have one decode pending.
   MockMediaCodecBridge* InitializeFully_OneDecodePending(
       VideoDecoderConfig config = TestVideoConfig::Large(kCodecH264)) {
-    InitializeWithSurfaceTexture_OneDecodePending(config);
+    InitializeWithTextureOwner_OneDecodePending(config);
     return codec_allocator_->ProvideMockCodecAsync();
   }
 
@@ -243,7 +242,7 @@
   std::unique_ptr<MockDeviceInfo> device_info_;
   std::unique_ptr<FakeCodecAllocator> codec_allocator_;
   MockAndroidVideoSurfaceChooser* surface_chooser_;
-  MockSurfaceTextureGLOwner* surface_texture_;
+  MockTextureOwner* texture_owner_;
   MockVideoFrameFactory* video_frame_factory_;
   NiceMock<base::MockCallback<VideoDecoder::DecodeCB>> decode_cb_;
   std::unique_ptr<DestructionObserver> destruction_observer_;
@@ -326,7 +325,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, OverlayInfoDuringInitUpdatesSurfaceChooser) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   EXPECT_CALL(*surface_chooser_, MockUpdateState());
   provide_overlay_info_cb_.Run(OverlayInfo());
 }
@@ -336,7 +335,7 @@
   mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
   provide_overlay_info_cb_.Run(OverlayInfo());
   EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
-  surface_chooser_->ProvideSurfaceTexture();
+  surface_chooser_->ProvideTextureOwner();
 }
 
 TEST_F(MediaCodecVideoDecoderTest, FrameFactoryInitFailureIsAnError) {
@@ -349,7 +348,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, CodecCreationFailureIsAnError) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
   EXPECT_CALL(decode_cb_, Run(DecodeStatus::DECODE_ERROR)).Times(2);
   // Failing to create a codec should put MCVD into an error state.
@@ -383,7 +382,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, SurfaceChooserIsUpdatedOnOverlayChanges) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
 
   EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory(_)).Times(2);
   OverlayInfo info;
@@ -396,7 +395,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, OverlayInfoUpdatesAreIgnoredInStateError) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   // Enter the error state.
   codec_allocator_->ProvideNullCodecAsync();
 
@@ -407,7 +406,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, DuplicateOverlayInfoUpdatesAreIgnored) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
 
   // The second overlay info update should be ignored.
   EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory(_)).Times(1);
@@ -481,7 +480,7 @@
   auto* codec = codec_allocator_->ProvideMockCodecAsync();
 
   // Set a pending surface transition and then call PumpCodec().
-  surface_chooser_->ProvideSurfaceTexture();
+  surface_chooser_->ProvideTextureOwner();
   EXPECT_CALL(*codec, SetSurface(_)).WillOnce(Return(true));
   PumpCodec();
 }
@@ -491,7 +490,7 @@
   InitializeWithOverlay_OneDecodePending();
   auto* codec = codec_allocator_->ProvideMockCodecAsync();
 
-  surface_chooser_->ProvideSurfaceTexture();
+  surface_chooser_->ProvideTextureOwner();
   EXPECT_CALL(*codec, SetSurface(_)).WillOnce(Return(false));
   EXPECT_CALL(decode_cb_, Run(DecodeStatus::DECODE_ERROR)).Times(2);
   EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(codec, NotNull(), _));
@@ -501,19 +500,19 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, SurfaceTransitionsCanBeCanceled) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   auto* codec = codec_allocator_->ProvideMockCodecAsync();
 
-  // Set a pending transition to an overlay, and then back to a surface texture.
+  // Set a pending transition to an overlay, and then back to a texture owner.
   // They should cancel each other out and leave the codec as-is.
   EXPECT_CALL(*codec, SetSurface(_)).Times(0);
   auto overlay = std::make_unique<MockAndroidOverlay>();
   auto observer = overlay->CreateDestructionObserver();
   surface_chooser_->ProvideOverlay(std::move(overlay));
 
-  // Switching back to surface texture should delete the pending overlay.
+  // Switching back to texture owner should delete the pending overlay.
   observer->ExpectDestruction();
-  surface_chooser_->ProvideSurfaceTexture();
+  surface_chooser_->ProvideTextureOwner();
   observer.reset();
 
   // Verify that Decode() does not transition the surface
@@ -521,23 +520,23 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, TransitionToSameSurfaceIsIgnored) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   auto* codec = codec_allocator_->ProvideMockCodecAsync();
   EXPECT_CALL(*codec, SetSurface(_)).Times(0);
-  surface_chooser_->ProvideSurfaceTexture();
+  surface_chooser_->ProvideTextureOwner();
   mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
 }
 
 TEST_F(MediaCodecVideoDecoderTest,
        ResetBeforeCodecInitializedSucceedsImmediately) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   base::MockCallback<base::Closure> reset_cb;
   EXPECT_CALL(reset_cb, Run());
   mcvd_->Reset(reset_cb.Get());
 }
 
 TEST_F(MediaCodecVideoDecoderTest, ResetAbortsPendingDecodes) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   EXPECT_CALL(decode_cb_, Run(DecodeStatus::ABORTED));
   mcvd_->Reset(base::DoNothing());
 }
@@ -680,7 +679,7 @@
 }
 
 TEST_F(MediaCodecVideoDecoderTest, TeardownInvalidatesCodecCreationWeakPtr) {
-  InitializeWithSurfaceTexture_OneDecodePending();
+  InitializeWithTextureOwner_OneDecodePending();
   destruction_observer_->DoNotAllowDestruction();
   mcvd_.reset();
   // DeleteSoon() is now pending. Ensure it's safe if the codec creation
diff --git a/media/gpu/android/mock_android_video_surface_chooser.cc b/media/gpu/android/mock_android_video_surface_chooser.cc
index 21641c8..35f701f 100644
--- a/media/gpu/android/mock_android_video_surface_chooser.cc
+++ b/media/gpu/android/mock_android_video_surface_chooser.cc
@@ -11,10 +11,10 @@
 
 void MockAndroidVideoSurfaceChooser::SetClientCallbacks(
     UseOverlayCB use_overlay_cb,
-    UseSurfaceTextureCB use_surface_texture_cb) {
+    UseTextureOwnerCB use_texture_owner_cb) {
   MockSetClientCallbacks();
   use_overlay_cb_ = std::move(use_overlay_cb);
-  use_surface_texture_cb_ = std::move(use_surface_texture_cb);
+  use_texture_owner_cb_ = std::move(use_texture_owner_cb);
 }
 
 void MockAndroidVideoSurfaceChooser::UpdateState(
@@ -28,8 +28,8 @@
   current_state_ = new_state;
 }
 
-void MockAndroidVideoSurfaceChooser::ProvideSurfaceTexture() {
-  use_surface_texture_cb_.Run();
+void MockAndroidVideoSurfaceChooser::ProvideTextureOwner() {
+  use_texture_owner_cb_.Run();
 }
 
 void MockAndroidVideoSurfaceChooser::ProvideOverlay(
diff --git a/media/gpu/android/mock_android_video_surface_chooser.h b/media/gpu/android/mock_android_video_surface_chooser.h
index 9f3fdc6..24a2322 100644
--- a/media/gpu/android/mock_android_video_surface_chooser.h
+++ b/media/gpu/android/mock_android_video_surface_chooser.h
@@ -12,7 +12,7 @@
 namespace media {
 
 // A mock surface chooser that lets tests choose the surface with
-// ProvideOverlay() and ProvideSurfaceTexture().
+// ProvideOverlay() and ProvideTextureOwner().
 class MockAndroidVideoSurfaceChooser : public AndroidVideoSurfaceChooser {
  public:
   MockAndroidVideoSurfaceChooser();
@@ -27,16 +27,16 @@
   MOCK_METHOD1(MockReplaceOverlayFactory, void(bool));
 
   void SetClientCallbacks(UseOverlayCB use_overlay_cb,
-                          UseSurfaceTextureCB use_surface_texture_cb) override;
+                          UseTextureOwnerCB use_texture_owner_cb) override;
   void UpdateState(base::Optional<AndroidOverlayFactoryCB> factory,
                    const State& new_state) override;
 
   // Calls the corresponding callback to choose the surface.
   void ProvideOverlay(std::unique_ptr<AndroidOverlay> overlay);
-  void ProvideSurfaceTexture();
+  void ProvideTextureOwner();
 
   UseOverlayCB use_overlay_cb_;
-  UseSurfaceTextureCB use_surface_texture_cb_;
+  UseTextureOwnerCB use_texture_owner_cb_;
   AndroidOverlayFactoryCB factory_;
   State current_state_;
 
diff --git a/media/gpu/android/mock_surface_texture_gl_owner.cc b/media/gpu/android/mock_surface_texture_gl_owner.cc
deleted file mode 100644
index 5411305..0000000
--- a/media/gpu/android/mock_surface_texture_gl_owner.cc
+++ /dev/null
@@ -1,39 +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 "media/gpu/android/mock_surface_texture_gl_owner.h"
-
-namespace media {
-
-using testing::Invoke;
-using testing::Return;
-
-MockSurfaceTextureGLOwner::MockSurfaceTextureGLOwner(
-    GLuint fake_texture_id,
-    gl::GLContext* fake_context,
-    gl::GLSurface* fake_surface)
-    : fake_texture_id(fake_texture_id),
-      fake_context(fake_context),
-      fake_surface(fake_surface),
-      expecting_frame_available(false) {
-  ON_CALL(*this, GetTextureId()).WillByDefault(Return(fake_texture_id));
-  ON_CALL(*this, GetContext()).WillByDefault(Return(fake_context));
-  ON_CALL(*this, GetSurface()).WillByDefault(Return(fake_surface));
-  ON_CALL(*this, SetReleaseTimeToNow())
-      .WillByDefault(
-          Invoke(this, &MockSurfaceTextureGLOwner::FakeSetReleaseTimeToNow));
-  ON_CALL(*this, IgnorePendingRelease())
-      .WillByDefault(
-          Invoke(this, &MockSurfaceTextureGLOwner::FakeIgnorePendingRelease));
-  ON_CALL(*this, IsExpectingFrameAvailable())
-      .WillByDefault(Invoke(
-          this, &MockSurfaceTextureGLOwner::FakeIsExpectingFrameAvailable));
-  ON_CALL(*this, WaitForFrameAvailable())
-      .WillByDefault(
-          Invoke(this, &MockSurfaceTextureGLOwner::FakeWaitForFrameAvailable));
-}
-
-MockSurfaceTextureGLOwner::~MockSurfaceTextureGLOwner() = default;
-
-}  // namespace media
diff --git a/media/gpu/android/mock_texture_owner.cc b/media/gpu/android/mock_texture_owner.cc
new file mode 100644
index 0000000..bd1368b
--- /dev/null
+++ b/media/gpu/android/mock_texture_owner.cc
@@ -0,0 +1,36 @@
+// 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 "media/gpu/android/mock_texture_owner.h"
+
+namespace media {
+
+using testing::Invoke;
+using testing::Return;
+
+MockTextureOwner::MockTextureOwner(GLuint fake_texture_id,
+                                   gl::GLContext* fake_context,
+                                   gl::GLSurface* fake_surface)
+    : fake_texture_id(fake_texture_id),
+      fake_context(fake_context),
+      fake_surface(fake_surface),
+      expecting_frame_available(false) {
+  ON_CALL(*this, GetTextureId()).WillByDefault(Return(fake_texture_id));
+  ON_CALL(*this, GetContext()).WillByDefault(Return(fake_context));
+  ON_CALL(*this, GetSurface()).WillByDefault(Return(fake_surface));
+  ON_CALL(*this, SetReleaseTimeToNow())
+      .WillByDefault(Invoke(this, &MockTextureOwner::FakeSetReleaseTimeToNow));
+  ON_CALL(*this, IgnorePendingRelease())
+      .WillByDefault(Invoke(this, &MockTextureOwner::FakeIgnorePendingRelease));
+  ON_CALL(*this, IsExpectingFrameAvailable())
+      .WillByDefault(
+          Invoke(this, &MockTextureOwner::FakeIsExpectingFrameAvailable));
+  ON_CALL(*this, WaitForFrameAvailable())
+      .WillByDefault(
+          Invoke(this, &MockTextureOwner::FakeWaitForFrameAvailable));
+}
+
+MockTextureOwner::~MockTextureOwner() = default;
+
+}  // namespace media
diff --git a/media/gpu/android/mock_surface_texture_gl_owner.h b/media/gpu/android/mock_texture_owner.h
similarity index 75%
rename from media/gpu/android/mock_surface_texture_gl_owner.h
rename to media/gpu/android/mock_texture_owner.h
index f43efe73..8f3d5e0 100644
--- a/media/gpu/android/mock_surface_texture_gl_owner.h
+++ b/media/gpu/android/mock_texture_owner.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_ANDROID_MOCK_SURFACE_TEXTURE_GL_OWNER_H_
-#define MEDIA_GPU_ANDROID_MOCK_SURFACE_TEXTURE_GL_OWNER_H_
+#ifndef MEDIA_GPU_ANDROID_MOCK_TEXTURE_OWNER_H_
+#define MEDIA_GPU_ANDROID_MOCK_TEXTURE_OWNER_H_
 
-#include "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_bindings.h"
@@ -15,11 +15,11 @@
 namespace media {
 
 // This is a mock with a small amount of fake functionality too.
-class MockSurfaceTextureGLOwner : public SurfaceTextureGLOwner {
+class MockTextureOwner : public TextureOwner {
  public:
-  MockSurfaceTextureGLOwner(GLuint fake_texture_id,
-                            gl::GLContext* fake_context,
-                            gl::GLSurface* fake_surface);
+  MockTextureOwner(GLuint fake_texture_id,
+                   gl::GLContext* fake_context,
+                   gl::GLSurface* fake_surface);
 
   MOCK_CONST_METHOD0(GetTextureId, GLuint());
   MOCK_CONST_METHOD0(GetContext, gl::GLContext*());
@@ -45,9 +45,9 @@
   bool expecting_frame_available;
 
  protected:
-  ~MockSurfaceTextureGLOwner();
+  ~MockTextureOwner();
 };
 
 }  // namespace media
 
-#endif  // MEDIA_GPU_ANDROID_MOCK_SURFACE_TEXTURE_GL_OWNER_H_
+#endif  // MEDIA_GPU_ANDROID_MOCK_TEXTURE_OWNER_H_
diff --git a/media/gpu/android/surface_chooser_helper.cc b/media/gpu/android/surface_chooser_helper.cc
index 1704289..d329090 100644
--- a/media/gpu/android/surface_chooser_helper.cc
+++ b/media/gpu/android/surface_chooser_helper.cc
@@ -143,8 +143,8 @@
   if (!is_using_overlay) {
     // Not an overlay.
     return surface_chooser_state_.is_secure
-               ? FrameInformation::SURFACETEXTURE_L3
-               : FrameInformation::SURFACETEXTURE_INSECURE;
+               ? FrameInformation::NON_OVERLAY_L3
+               : FrameInformation::NON_OVERLAY_INSECURE;
   }
 
   // Overlay.
diff --git a/media/gpu/android/surface_chooser_helper.h b/media/gpu/android/surface_chooser_helper.h
index 2bfaddf9..907d077 100644
--- a/media/gpu/android/surface_chooser_helper.h
+++ b/media/gpu/android/surface_chooser_helper.h
@@ -39,11 +39,11 @@
 
   enum class SecureSurfaceMode {
     // The surface should not be secure.  This allows both overlays and
-    // SurfaceTexture surfaces.
+    // TextureOwner surfaces.
     kInsecure,
 
     // It is preferable to have a secure surface, but insecure
-    // (SurfaceTexture) is better than failing.
+    // (TextureOwner) is better than failing.
     kRequested,
 
     // The surface must be a secure surface, and should fail otherwise.
@@ -53,8 +53,8 @@
   // Must match AVDAFrameInformation UMA enum.  Please do not remove or re-order
   // values, only append new ones.
   enum class FrameInformation {
-    SURFACETEXTURE_INSECURE = 0,
-    SURFACETEXTURE_L3 = 1,
+    NON_OVERLAY_INSECURE = 0,
+    NON_OVERLAY_L3 = 1,
     OVERLAY_L3 = 2,
     OVERLAY_L1 = 3,
     OVERLAY_INSECURE_PLAYER_ELEMENT_FULLSCREEN = 4,
diff --git a/media/gpu/android/surface_chooser_helper_unittest.cc b/media/gpu/android/surface_chooser_helper_unittest.cc
index e1d7c92..06b74db 100644
--- a/media/gpu/android/surface_chooser_helper_unittest.cc
+++ b/media/gpu/android/surface_chooser_helper_unittest.cc
@@ -242,7 +242,7 @@
 
   ASSERT_EQ(SurfaceChooserHelper::FrameInformation::OVERLAY_L3,
             helper_->ComputeFrameInformation(true));
-  ASSERT_EQ(SurfaceChooserHelper::FrameInformation::SURFACETEXTURE_L3,
+  ASSERT_EQ(SurfaceChooserHelper::FrameInformation::NON_OVERLAY_L3,
             helper_->ComputeFrameInformation(false));
 }
 
@@ -251,8 +251,8 @@
   helper_->SetSecureSurfaceMode(
       SurfaceChooserHelper::SecureSurfaceMode::kInsecure);
 
-  // Not using an overlay should be SURFACETEXTURE_INSECURE
-  ASSERT_EQ(SurfaceChooserHelper::FrameInformation::SURFACETEXTURE_INSECURE,
+  // Not using an overlay should be NON_OVERLAY_INSECURE
+  ASSERT_EQ(SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE,
             helper_->ComputeFrameInformation(false));
 
   // Fullscreen state should affect the result, so that we can tell the
diff --git a/media/gpu/android/surface_texture_gl_owner_unittest.cc b/media/gpu/android/surface_texture_gl_owner_unittest.cc
index 1d395cb..a8898b9 100644
--- a/media/gpu/android/surface_texture_gl_owner_unittest.cc
+++ b/media/gpu/android/surface_texture_gl_owner_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 "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 
 #include <stdint.h>
 
@@ -43,7 +43,7 @@
     context_->Initialize(surface_.get(), gl::GLContextAttribs());
     ASSERT_TRUE(context_->MakeCurrent(surface_.get()));
 
-    surface_texture_ = SurfaceTextureGLOwnerImpl::Create();
+    surface_texture_ = SurfaceTextureGLOwner::Create();
     texture_id_ = surface_texture_->GetTextureId();
     // Bind and un-bind the texture, since that's required for glIsTexture to
     // return true.
@@ -60,7 +60,7 @@
     gl::init::ShutdownGL(false);
   }
 
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+  scoped_refptr<TextureOwner> surface_texture_;
   GLuint texture_id_ = 0;
 
   scoped_refptr<gl::GLContext> context_;
diff --git a/media/gpu/android/surface_texture_gl_owner.cc b/media/gpu/android/texture_owner.cc
similarity index 77%
rename from media/gpu/android/surface_texture_gl_owner.cc
rename to media/gpu/android/texture_owner.cc
index f4b4d5b..7dd014b 100644
--- a/media/gpu/android/surface_texture_gl_owner.cc
+++ b/media/gpu/android/texture_owner.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 "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -30,14 +30,14 @@
   ~FrameAvailableEvent() = default;
 };
 
-SurfaceTextureGLOwner::SurfaceTextureGLOwner()
-    : base::RefCountedDeleteOnSequence<SurfaceTextureGLOwner>(
+TextureOwner::TextureOwner()
+    : base::RefCountedDeleteOnSequence<TextureOwner>(
           base::ThreadTaskRunnerHandle::Get()),
       task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
 
-SurfaceTextureGLOwner::~SurfaceTextureGLOwner() = default;
+TextureOwner::~TextureOwner() = default;
 
-scoped_refptr<SurfaceTextureGLOwner> SurfaceTextureGLOwnerImpl::Create() {
+scoped_refptr<TextureOwner> SurfaceTextureGLOwner::Create() {
   GLuint texture_id;
   glGenTextures(1, &texture_id);
   if (!texture_id)
@@ -52,10 +52,10 @@
   glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
 
-  return new SurfaceTextureGLOwnerImpl(texture_id);
+  return new SurfaceTextureGLOwner(texture_id);
 }
 
-SurfaceTextureGLOwnerImpl::SurfaceTextureGLOwnerImpl(GLuint texture_id)
+SurfaceTextureGLOwner::SurfaceTextureGLOwner(GLuint texture_id)
     : surface_texture_(gl::SurfaceTexture::Create(texture_id)),
       texture_id_(texture_id),
       context_(gl::GLContext::GetCurrent()),
@@ -63,11 +63,11 @@
       frame_available_event_(new FrameAvailableEvent()) {
   DCHECK(context_);
   DCHECK(surface_);
-  surface_texture_->SetFrameAvailableCallbackOnAnyThread(
-      base::Bind(&FrameAvailableEvent::Signal, frame_available_event_));
+  surface_texture_->SetFrameAvailableCallbackOnAnyThread(base::BindRepeating(
+      &FrameAvailableEvent::Signal, frame_available_event_));
 }
 
-SurfaceTextureGLOwnerImpl::~SurfaceTextureGLOwnerImpl() {
+SurfaceTextureGLOwner::~SurfaceTextureGLOwner() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Make sure that the SurfaceTexture isn't using the GL objects.
@@ -80,56 +80,56 @@
   }
 }
 
-GLuint SurfaceTextureGLOwnerImpl::GetTextureId() const {
+GLuint SurfaceTextureGLOwner::GetTextureId() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return texture_id_;
 }
 
-gl::ScopedJavaSurface SurfaceTextureGLOwnerImpl::CreateJavaSurface() const {
+gl::ScopedJavaSurface SurfaceTextureGLOwner::CreateJavaSurface() const {
   return gl::ScopedJavaSurface(surface_texture_.get());
 }
 
-void SurfaceTextureGLOwnerImpl::UpdateTexImage() {
+void SurfaceTextureGLOwner::UpdateTexImage() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   surface_texture_->UpdateTexImage();
 }
 
-void SurfaceTextureGLOwnerImpl::GetTransformMatrix(float mtx[]) {
+void SurfaceTextureGLOwner::GetTransformMatrix(float mtx[]) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   surface_texture_->GetTransformMatrix(mtx);
 }
 
-void SurfaceTextureGLOwnerImpl::ReleaseBackBuffers() {
+void SurfaceTextureGLOwner::ReleaseBackBuffers() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   surface_texture_->ReleaseBackBuffers();
 }
 
-gl::GLContext* SurfaceTextureGLOwnerImpl::GetContext() const {
+gl::GLContext* SurfaceTextureGLOwner::GetContext() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return context_.get();
 }
 
-gl::GLSurface* SurfaceTextureGLOwnerImpl::GetSurface() const {
+gl::GLSurface* SurfaceTextureGLOwner::GetSurface() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return surface_.get();
 }
 
-void SurfaceTextureGLOwnerImpl::SetReleaseTimeToNow() {
+void SurfaceTextureGLOwner::SetReleaseTimeToNow() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   release_time_ = base::TimeTicks::Now();
 }
 
-void SurfaceTextureGLOwnerImpl::IgnorePendingRelease() {
+void SurfaceTextureGLOwner::IgnorePendingRelease() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   release_time_ = base::TimeTicks();
 }
 
-bool SurfaceTextureGLOwnerImpl::IsExpectingFrameAvailable() {
+bool SurfaceTextureGLOwner::IsExpectingFrameAvailable() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return !release_time_.is_null();
 }
 
-void SurfaceTextureGLOwnerImpl::WaitForFrameAvailable() {
+void SurfaceTextureGLOwner::WaitForFrameAvailable() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!release_time_.is_null());
 
diff --git a/media/gpu/android/surface_texture_gl_owner.h b/media/gpu/android/texture_owner.h
similarity index 79%
rename from media/gpu/android/surface_texture_gl_owner.h
rename to media/gpu/android/texture_owner.h
index 904dc34..f07ca23 100644
--- a/media/gpu/android/surface_texture_gl_owner.h
+++ b/media/gpu/android/texture_owner.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_ANDROID_SURFACE_TEXTURE_GL_OWNER_H_
-#define MEDIA_GPU_ANDROID_SURFACE_TEXTURE_GL_OWNER_H_
+#ifndef MEDIA_GPU_ANDROID_TEXTURE_OWNER_H_
+#define MEDIA_GPU_ANDROID_TEXTURE_OWNER_H_
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_delete_on_sequence.h"
@@ -29,22 +29,22 @@
 // exception of CreateJavaSurface(), which can be called on any thread.
 // It's safe to keep and drop refptrs to it on any thread; it will be
 // automatically destructed on the thread it was constructed on.
-// Virtual for testing; see SurfaceTextureGLOwnerImpl.
-class MEDIA_GPU_EXPORT SurfaceTextureGLOwner
-    : public base::RefCountedDeleteOnSequence<SurfaceTextureGLOwner> {
+// Virtual for testing; see SurfaceTextureGLOwner.
+class MEDIA_GPU_EXPORT TextureOwner
+    : public base::RefCountedDeleteOnSequence<TextureOwner> {
  public:
-  SurfaceTextureGLOwner();
+  TextureOwner();
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
     return task_runner_;
   }
 
-  // Returns the GL texture id that the SurfaceTexture is attached to.
+  // Returns the GL texture id that the TextureOwner is attached to.
   virtual GLuint GetTextureId() const = 0;
   virtual gl::GLContext* GetContext() const = 0;
   virtual gl::GLSurface* GetSurface() const = 0;
 
-  // Create a java surface for the SurfaceTexture.
+  // Create a java surface for the TextureOwner.
   virtual gl::ScopedJavaSurface CreateJavaSurface() const = 0;
 
   // See gl::SurfaceTexture for the following.
@@ -73,22 +73,21 @@
   virtual void WaitForFrameAvailable() = 0;
 
  protected:
-  friend class base::RefCountedDeleteOnSequence<SurfaceTextureGLOwner>;
-  friend class base::DeleteHelper<SurfaceTextureGLOwner>;
-  virtual ~SurfaceTextureGLOwner();
+  friend class base::RefCountedDeleteOnSequence<TextureOwner>;
+  friend class base::DeleteHelper<TextureOwner>;
+  virtual ~TextureOwner();
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  DISALLOW_COPY_AND_ASSIGN(SurfaceTextureGLOwner);
+  DISALLOW_COPY_AND_ASSIGN(TextureOwner);
 };
 
-class MEDIA_GPU_EXPORT SurfaceTextureGLOwnerImpl
-    : public SurfaceTextureGLOwner {
+class MEDIA_GPU_EXPORT SurfaceTextureGLOwner : public TextureOwner {
  public:
   // Creates a GL texture using the current platform GL context and returns a
-  // new SurfaceTextureGLOwnerImpl attached to it. Returns null on failure.
-  static scoped_refptr<SurfaceTextureGLOwner> Create();
+  // new SurfaceTextureGLOwner attached to it. Returns null on failure.
+  static scoped_refptr<TextureOwner> Create();
 
   GLuint GetTextureId() const override;
   gl::GLContext* GetContext() const override;
@@ -103,8 +102,8 @@
   void WaitForFrameAvailable() override;
 
  private:
-  SurfaceTextureGLOwnerImpl(GLuint texture_id);
-  ~SurfaceTextureGLOwnerImpl() override;
+  SurfaceTextureGLOwner(GLuint texture_id);
+  ~SurfaceTextureGLOwner() override;
 
   scoped_refptr<gl::SurfaceTexture> surface_texture_;
   GLuint texture_id_;
@@ -121,9 +120,9 @@
 
   THREAD_CHECKER(thread_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(SurfaceTextureGLOwnerImpl);
+  DISALLOW_COPY_AND_ASSIGN(SurfaceTextureGLOwner);
 };
 
 }  // namespace media
 
-#endif  // MEDIA_GPU_ANDROID_SURFACE_TEXTURE_GL_OWNER_H_
+#endif  // MEDIA_GPU_ANDROID_TEXTURE_OWNER_H_
diff --git a/media/gpu/android/video_frame_factory.h b/media/gpu/android/video_frame_factory.h
index ee65a1d..f4507e8d 100644
--- a/media/gpu/android/video_frame_factory.h
+++ b/media/gpu/android/video_frame_factory.h
@@ -23,7 +23,7 @@
 
 struct AVDASurfaceBundle;
 class CodecOutputBuffer;
-class SurfaceTextureGLOwner;
+class TextureOwner;
 class VideoFrame;
 
 // VideoFrameFactory creates CodecOutputBuffer backed VideoFrames. Not thread
@@ -31,13 +31,13 @@
 class MEDIA_GPU_EXPORT VideoFrameFactory {
  public:
   using GetStubCb = base::Callback<gpu::CommandBufferStub*()>;
-  using InitCb = base::Callback<void(scoped_refptr<SurfaceTextureGLOwner>)>;
+  using InitCb = base::RepeatingCallback<void(scoped_refptr<TextureOwner>)>;
 
   VideoFrameFactory() = default;
   virtual ~VideoFrameFactory() = default;
 
   // Initializes the factory and runs |init_cb| on the current thread when it's
-  // complete. If initialization fails, the returned surface texture will be
+  // complete. If initialization fails, the returned texture owner will be
   // null.  |wants_promotion_hint| tells us whether to mark VideoFrames for
   // compositor overlay promotion hints or not.
   virtual void Initialize(bool wants_promotion_hint, InitCb init_cb) = 0;
@@ -47,8 +47,8 @@
   virtual void SetSurfaceBundle(
       scoped_refptr<AVDASurfaceBundle> surface_bundle) = 0;
 
-  // Creates a new VideoFrame backed by |output_buffer| and |surface_texture|.
-  // |surface_texture| may be null if the buffer is backed by an overlay
+  // Creates a new VideoFrame backed by |output_buffer| and |texture_owner|.
+  // |texture_owner| may be null if the buffer is backed by an overlay
   // instead. Runs |output_cb| on the calling sequence to return the frame.
   // TODO(liberato): update the comment.
   virtual void CreateVideoFrame(
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index 2bf885f..683aa89 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -67,11 +67,11 @@
   scoped_refptr<CodecImageGroup> image_group;
   if (!surface_bundle) {
     // Clear everything, just so we're not holding a reference.
-    surface_texture_ = nullptr;
+    texture_owner_ = nullptr;
   } else {
-    // If |surface_bundle| is using a SurfaceTexture, then get it.
-    surface_texture_ =
-        surface_bundle->overlay ? nullptr : surface_bundle->surface_texture;
+    // If |surface_bundle| is using a TextureOwner, then get it.
+    texture_owner_ =
+        surface_bundle->overlay ? nullptr : surface_bundle->texture_owner_;
 
     // Start a new image group.  Note that there's no reason that we can't have
     // more than one group per surface bundle; it's okay if we're called
@@ -103,7 +103,7 @@
       FROM_HERE,
       base::Bind(&GpuVideoFrameFactory::CreateVideoFrame,
                  base::Unretained(gpu_video_frame_factory_.get()),
-                 base::Passed(&output_buffer), surface_texture_, timestamp,
+                 base::Passed(&output_buffer), texture_owner_, timestamp,
                  natural_size, std::move(promotion_hint_cb),
                  std::move(output_cb), base::ThreadTaskRunnerHandle::Get()));
 }
@@ -126,7 +126,7 @@
     stub_->RemoveDestructionObserver(this);
 }
 
-scoped_refptr<SurfaceTextureGLOwner> GpuVideoFrameFactory::Initialize(
+scoped_refptr<TextureOwner> GpuVideoFrameFactory::Initialize(
     bool wants_promotion_hint,
     VideoFrameFactoryImpl::GetStubCb get_stub_cb) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -140,12 +140,12 @@
       new TexturePool(std::make_unique<CommandBufferStubWrapperImpl>(stub_));
 
   decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
-  return SurfaceTextureGLOwnerImpl::Create();
+  return SurfaceTextureGLOwner::Create();
 }
 
 void GpuVideoFrameFactory::CreateVideoFrame(
     std::unique_ptr<CodecOutputBuffer> output_buffer,
-    scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+    scoped_refptr<TextureOwner> texture_owner_,
     base::TimeDelta timestamp,
     gfx::Size natural_size,
     PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
@@ -154,7 +154,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   scoped_refptr<VideoFrame> frame;
   scoped_refptr<gpu::gles2::TextureRef> texture_ref;
-  CreateVideoFrameInternal(std::move(output_buffer), std::move(surface_texture),
+  CreateVideoFrameInternal(std::move(output_buffer), std::move(texture_owner_),
                            timestamp, natural_size,
                            std::move(promotion_hint_cb), &frame, &texture_ref);
   if (!frame || !texture_ref)
@@ -183,7 +183,7 @@
 
 void GpuVideoFrameFactory::CreateVideoFrameInternal(
     std::unique_ptr<CodecOutputBuffer> output_buffer,
-    scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+    scoped_refptr<TextureOwner> texture_owner_,
     base::TimeDelta timestamp,
     gfx::Size natural_size,
     PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
@@ -219,27 +219,27 @@
                                      size.width(), size.height(), GL_RGBA,
                                      GL_UNSIGNED_BYTE);
   auto image = base::MakeRefCounted<CodecImage>(
-      std::move(output_buffer), surface_texture, std::move(promotion_hint_cb));
+      std::move(output_buffer), texture_owner_, std::move(promotion_hint_cb));
   images_.push_back(image.get());
 
   // Add |image| to our current image group.  This makes suer that any overlay
-  // lasts as long as the images.  For SurfaceTexture, it doesn't do much.
+  // lasts as long as the images.  For TextureOwner, it doesn't do much.
   image_group_->AddCodecImage(image.get());
 
   // Attach the image to the texture.
-  // If we're attaching a SurfaceTexture backed image, we set the state to
+  // If we're attaching a TextureOwner backed image, we set the state to
   // UNBOUND. This ensures that the implementation will call CopyTexImage()
-  // which lets us update the surface texture at the right time.
+  // which lets us update the texture owner at the right time.
   // For overlays we set the state to BOUND because it's required for
   // ScheduleOverlayPlane() to be called. If something tries to sample from an
   // overlay texture it won't work, but there's no way to make that work.
-  auto image_state = surface_texture ? gpu::gles2::Texture::UNBOUND
-                                     : gpu::gles2::Texture::BOUND;
-  GLuint surface_texture_service_id =
-      surface_texture ? surface_texture->GetTextureId() : 0;
+  auto image_state = texture_owner_ ? gpu::gles2::Texture::UNBOUND
+                                    : gpu::gles2::Texture::BOUND;
+  GLuint texture_owner_service_id =
+      texture_owner_ ? texture_owner_->GetTextureId() : 0;
   texture_manager->SetLevelStreamTextureImage(
       texture_ref.get(), GL_TEXTURE_EXTERNAL_OES, 0, image.get(), image_state,
-      surface_texture_service_id);
+      texture_owner_service_id);
   texture_manager->SetLevelCleared(texture_ref.get(), GL_TEXTURE_EXTERNAL_OES,
                                    0, true);
 
@@ -258,16 +258,16 @@
     frame->metadata()->SetBoolean(VideoFrameMetadata::COPY_REQUIRED, true);
 
   // We unconditionally mark the picture as overlayable, even if
-  // |!surface_texture|, if we want to get hints.  It's required, else we won't
+  // |!texture_owner_|, if we want to get hints.  It's required, else we won't
   // get hints.
-  const bool allow_overlay = !surface_texture || wants_promotion_hint_;
+  const bool allow_overlay = !texture_owner_ || wants_promotion_hint_;
 
   frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY,
                                 allow_overlay);
   frame->metadata()->SetBoolean(VideoFrameMetadata::WANTS_PROMOTION_HINT,
                                 wants_promotion_hint_);
-  frame->metadata()->SetBoolean(VideoFrameMetadata::SURFACE_TEXTURE,
-                                !!surface_texture);
+  frame->metadata()->SetBoolean(VideoFrameMetadata::TEXTURE_OWNER,
+                                !!texture_owner_);
 
   *video_frame_out = std::move(frame);
   *texture_ref_out = std::move(texture_ref);
diff --git a/media/gpu/android/video_frame_factory_impl.h b/media/gpu/android/video_frame_factory_impl.h
index 56592129..b8b7621 100644
--- a/media/gpu/android/video_frame_factory_impl.h
+++ b/media/gpu/android/video_frame_factory_impl.h
@@ -13,7 +13,7 @@
 #include "media/base/video_frame.h"
 #include "media/gpu/android/codec_image.h"
 #include "media/gpu/android/codec_wrapper.h"
-#include "media/gpu/android/surface_texture_gl_owner.h"
+#include "media/gpu/android/texture_owner.h"
 #include "media/gpu/android/video_frame_factory.h"
 #include "media/gpu/gles2_decoder_helper.h"
 #include "media/gpu/media_gpu_export.h"
@@ -54,8 +54,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
   GetStubCb get_stub_cb_;
 
-  // The surface texture that video frames should use, or nullptr.
-  scoped_refptr<SurfaceTextureGLOwner> surface_texture_;
+  // The texture owner that video frames should use, or nullptr.
+  scoped_refptr<TextureOwner> texture_owner_;
 
   SEQUENCE_CHECKER(sequence_checker_);
   DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryImpl);
@@ -69,14 +69,14 @@
   GpuVideoFrameFactory();
   ~GpuVideoFrameFactory() override;
 
-  scoped_refptr<SurfaceTextureGLOwner> Initialize(
+  scoped_refptr<TextureOwner> Initialize(
       bool wants_promotion_hint,
       VideoFrameFactory::GetStubCb get_stub_cb);
 
   // Creates and returns a VideoFrame with its ReleaseMailboxCB.
   void CreateVideoFrame(
       std::unique_ptr<CodecOutputBuffer> output_buffer,
-      scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+      scoped_refptr<TextureOwner> texture_owner,
       base::TimeDelta timestamp,
       gfx::Size natural_size,
       PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
@@ -91,7 +91,7 @@
   // Creates a TextureRef and VideoFrame.
   void CreateVideoFrameInternal(
       std::unique_ptr<CodecOutputBuffer> output_buffer,
-      scoped_refptr<SurfaceTextureGLOwner> surface_texture,
+      scoped_refptr<TextureOwner> texture_owner,
       base::TimeDelta timestamp,
       gfx::Size natural_size,
       PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
@@ -167,8 +167,8 @@
   // Try to render the image following the front buffer to the back buffer.
   size_t back_buffer_index = *front_buffer_index + 1;
   if (back_buffer_index < images.size() &&
-      images[back_buffer_index]->is_surface_texture_backed()) {
-    images[back_buffer_index]->RenderToSurfaceTextureBackBuffer();
+      images[back_buffer_index]->is_texture_owner_backed()) {
+    images[back_buffer_index]->RenderToTextureOwnerBackBuffer();
   }
 }
 
diff --git a/media/gpu/android/video_frame_factory_impl_unittest.cc b/media/gpu/android/video_frame_factory_impl_unittest.cc
index 9125ae4..11fb25a3 100644
--- a/media/gpu/android/video_frame_factory_impl_unittest.cc
+++ b/media/gpu/android/video_frame_factory_impl_unittest.cc
@@ -13,15 +13,15 @@
 using testing::Return;
 
 // The dimensions for specifying MockImage behavior.
-enum ImageKind { kSurfaceTexture, kOverlay };
+enum ImageKind { kTextureOwner, kOverlay };
 enum Phase { kInCodec, kInFrontBuffer, kInvalidated };
 enum Expectation { kRenderToFrontBuffer, kRenderToBackBuffer, kNone };
 
 // A mock image with the same interface as CodecImage.
 struct MockImage {
   MockImage(ImageKind kind, Phase phase, Expectation expectation) {
-    ON_CALL(*this, is_surface_texture_backed())
-        .WillByDefault(Return(kind == kSurfaceTexture));
+    ON_CALL(*this, is_texture_owner_backed())
+        .WillByDefault(Return(kind == kTextureOwner));
 
     ON_CALL(*this, was_rendered_to_front_buffer())
         .WillByDefault(Return(phase == kInFrontBuffer));
@@ -34,17 +34,17 @@
     }
 
     if (expectation == kRenderToBackBuffer) {
-      EXPECT_CALL(*this, RenderToSurfaceTextureBackBuffer())
+      EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer())
           .WillOnce(Return(phase != kInvalidated));
     } else {
-      EXPECT_CALL(*this, RenderToSurfaceTextureBackBuffer()).Times(0);
+      EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer()).Times(0);
     }
   }
 
   MOCK_METHOD0(was_rendered_to_front_buffer, bool());
-  MOCK_METHOD0(is_surface_texture_backed, bool());
+  MOCK_METHOD0(is_texture_owner_backed, bool());
   MOCK_METHOD0(RenderToFrontBuffer, bool());
-  MOCK_METHOD0(RenderToSurfaceTextureBackBuffer, bool());
+  MOCK_METHOD0(RenderToTextureOwnerBackBuffer, bool());
 };
 
 class MaybeRenderEarlyTest : public testing::Test {
@@ -67,7 +67,7 @@
 }
 
 TEST_F(MaybeRenderEarlyTest, SingleUnrenderedSTImageIsRendered) {
-  AddImage(kSurfaceTexture, kInCodec, Expectation::kRenderToFrontBuffer);
+  AddImage(kTextureOwner, kInCodec, Expectation::kRenderToFrontBuffer);
   internal::MaybeRenderEarly(&images_);
 }
 
@@ -77,9 +77,9 @@
 }
 
 TEST_F(MaybeRenderEarlyTest, InvalidatedImagesAreSkippedOver) {
-  AddImage(kSurfaceTexture, kInvalidated, Expectation::kRenderToFrontBuffer);
-  AddImage(kSurfaceTexture, kInvalidated, Expectation::kRenderToFrontBuffer);
-  AddImage(kSurfaceTexture, kInCodec, Expectation::kRenderToFrontBuffer);
+  AddImage(kTextureOwner, kInvalidated, Expectation::kRenderToFrontBuffer);
+  AddImage(kTextureOwner, kInvalidated, Expectation::kRenderToFrontBuffer);
+  AddImage(kTextureOwner, kInCodec, Expectation::kRenderToFrontBuffer);
   internal::MaybeRenderEarly(&images_);
 }
 
@@ -92,10 +92,10 @@
 
 TEST_F(MaybeRenderEarlyTest,
        ImageFollowingLatestFrontBufferIsBackBufferRendered) {
-  AddImage(kSurfaceTexture, kInCodec, Expectation::kNone);
-  AddImage(kSurfaceTexture, kInFrontBuffer, Expectation::kNone);
-  AddImage(kSurfaceTexture, kInCodec, Expectation::kRenderToBackBuffer);
-  AddImage(kSurfaceTexture, kInCodec, Expectation::kNone);
+  AddImage(kTextureOwner, kInCodec, Expectation::kNone);
+  AddImage(kTextureOwner, kInFrontBuffer, Expectation::kNone);
+  AddImage(kTextureOwner, kInCodec, Expectation::kRenderToBackBuffer);
+  AddImage(kTextureOwner, kInCodec, Expectation::kNone);
   internal::MaybeRenderEarly(&images_);
 }
 
diff --git a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
index 2132f03..c8de1270 100644
--- a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
+++ b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
@@ -263,7 +263,7 @@
                   params.visible_rect, params.color_space,
                   params.allow_overlay);
   picture.set_size_changed(params.size_changed);
-  picture.set_surface_texture(params.surface_texture);
+  picture.set_texture_owner(params.surface_texture);
   picture.set_wants_promotion_hint(params.wants_promotion_hint);
   client_->PictureReady(picture);
 }
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index 33c4c89..126d1046 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -268,7 +268,7 @@
   params.color_space = picture.color_space();
   params.allow_overlay = picture.allow_overlay();
   params.size_changed = picture.size_changed();
-  params.surface_texture = picture.surface_texture();
+  params.surface_texture = picture.texture_owner();
   params.wants_promotion_hint = picture.wants_promotion_hint();
   if (!Send(new AcceleratedVideoDecoderHostMsg_PictureReady(host_route_id_,
                                                             params))) {
diff --git a/media/gpu/ipc/service/picture_buffer_manager.cc b/media/gpu/ipc/service/picture_buffer_manager.cc
index 2ecdb51c..06d743df 100644
--- a/media/gpu/ipc/service/picture_buffer_manager.cc
+++ b/media/gpu/ipc/service/picture_buffer_manager.cc
@@ -167,7 +167,7 @@
                                              gfx::Size natural_size) override {
     DVLOG(2) << __func__ << "(" << picture.picture_buffer_id() << ")";
     DCHECK(!picture.size_changed());
-    DCHECK(!picture.surface_texture());
+    DCHECK(!picture.texture_owner());
     DCHECK(!picture.wants_promotion_hint());
 
     base::AutoLock lock(picture_buffers_lock_);
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index bfad3c2..672a225 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -122,11 +122,10 @@
 // the specified number of times. In different test cases, we have different
 // values for |num_play_throughs|. This setting will override the value. A
 // special value "0" means no override.
-int g_num_play_throughs = 0;
+size_t g_num_play_throughs = 0;
 
-// TODO(hiroh): Change this type to bool
 // Fake decode
-int g_fake_decoder = 0;
+bool g_fake_decoder = 0;
 
 // Test buffer import into VDA, providing buffers allocated by us, instead of
 // requesting the VDA itself to allocate buffers.
@@ -143,15 +142,15 @@
 // Environment to store rendering thread.
 media::test::VideoDecodeAcceleratorTestEnvironment* g_env;
 
-const int kMaxResetAfterFrameNum = 100;
-const int kMaxFramesToDelayReuse = 64;
+constexpr size_t kMaxResetAfterFrameNum = 100;
+constexpr size_t kMaxFramesToDelayReuse = 64;
 const base::TimeDelta kReuseDelay = base::TimeDelta::FromSeconds(1);
 // Simulate WebRTC and call VDA::Decode 30 times per second.
-const int kWebRtcDecodeCallsPerSecond = 30;
+constexpr size_t kWebRtcDecodeCallsPerSecond = 30;
 // Simulate an adjustment to a larger number of pictures to make sure the
 // decoder supports an upwards adjustment.
-const int kExtraPictureBuffers = 2;
-const int kNoMidStreamReset = -1;
+constexpr size_t kExtraPictureBuffers = 2;
+constexpr size_t kNoMidStreamReset = std::numeric_limits<size_t>::max();
 
 const gfx::Size kThumbnailsPageSize(1600, 1200);
 const gfx::Size kThumbnailSize(160, 120);
@@ -159,7 +158,7 @@
 // We assert a minimal number of concurrent decoders we expect to succeed.
 // Different platforms can support more concurrent decoders, so we don't assert
 // failure above this.
-const size_t kMinSupportedNumConcurrentDecoders = 3;
+constexpr size_t kMinSupportedNumConcurrentDecoders = 3;
 
 // Magic constants for differentiating the reasons for NotifyResetDone being
 // called.
@@ -197,24 +196,24 @@
 struct TestVideoFile {
   explicit TestVideoFile(base::FilePath::StringType file_name)
       : file_name(file_name),
-        width(-1),
-        height(-1),
-        num_frames(-1),
-        num_fragments(-1),
-        min_fps_render(-1),
-        min_fps_no_render(-1),
+        width(0),
+        height(0),
+        num_frames(0),
+        num_fragments(0),
+        min_fps_render(0),
+        min_fps_no_render(0),
         profile(VIDEO_CODEC_PROFILE_UNKNOWN),
-        reset_after_frame_num(-1) {}
+        reset_after_frame_num(std::numeric_limits<size_t>::max()) {}
 
   base::FilePath::StringType file_name;
   int width;
   int height;
-  int num_frames;
-  int num_fragments;
-  int min_fps_render;
-  int min_fps_no_render;
+  size_t num_frames;
+  size_t num_fragments;
+  double min_fps_render;
+  double min_fps_no_render;
   VideoCodecProfile profile;
-  int reset_after_frame_num;
+  size_t reset_after_frame_num;
   std::string data_str;
 };
 
@@ -237,7 +236,6 @@
     : public VideoDecodeAccelerator::Client,
       public base::SupportsWeakPtr<GLRenderingVDAClient> {
  public:
-  // TODO(hiroh): Change some int type variables to size_t or uint32/64_t.
   // |window_id| the window_id of the client, which is used to identify the
   // rendering area in the |rendering_helper_|.
   // |num_in_flight_decodes| is the number of concurrent in-flight Decode()
@@ -264,16 +262,19 @@
   // as thumbnails at the end of tests.
   struct Config {
     size_t window_id = 0;
-    int num_in_flight_decodes = 1;
-    int num_play_throughs = 1;
+    size_t num_in_flight_decodes = 1;
+    size_t num_play_throughs = 1;
     ResetPoint reset_point = END_OF_STREAM_RESET;
-    int reset_after_frame_num = kNoMidStreamReset;
+    size_t reset_after_frame_num = kNoMidStreamReset;
+    // TODO(hiroh): Refactor as delete_decoder_state can be enum class.
+    // This can be set to not only ClientState, but also an integer in
+    // TearDownTiming test case.
     int delete_decoder_state = CS_RESET;
     gfx::Size frame_size;
     VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
-    int fake_decoder = 0;
-    int delay_reuse_after_frame_num = std::numeric_limits<int>::max();
-    int decode_calls_per_second = 0;
+    bool fake_decoder = false;
+    size_t delay_reuse_after_frame_num = std::numeric_limits<size_t>::max();
+    size_t decode_calls_per_second = 0;
     bool render_as_thumbnails = false;
   };
 
@@ -304,12 +305,12 @@
   void OutputFrameDeliveryTimes(base::File* output);
 
   // Simple getters for inspecting the state of the Client.
-  int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; }
-  int num_skipped_fragments() {
+  size_t num_done_bitstream_buffers() { return num_done_bitstream_buffers_; }
+  size_t num_skipped_fragments() {
     return encoded_data_helper_->num_skipped_fragments();
   }
-  int num_queued_fragments() { return num_queued_fragments_; }
-  int num_decoded_frames() { return num_decoded_frames_; }
+  size_t num_queued_fragments() { return num_queued_fragments_; }
+  size_t num_decoded_frames() { return num_decoded_frames_; }
   double frames_per_second();
   // Return the median of the decode time of all decoded frames.
   base::TimeDelta decode_time_median();
@@ -334,7 +335,7 @@
   const Config config_;
   RenderingHelper* const rendering_helper_;
   gfx::Size frame_size_;
-  int outstanding_decodes_;
+  size_t outstanding_decodes_;
   int next_bitstream_buffer_id_;
   ClientStateNotification<ClientState>* const note_;
   std::unique_ptr<VideoDecodeAccelerator> decoder_;
@@ -342,12 +343,12 @@
   std::unique_ptr<base::WeakPtrFactory<VideoDecodeAccelerator>>
       weak_vda_ptr_factory_;
   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> vda_factory_;
-  int remaining_play_throughs_;
+  size_t remaining_play_throughs_;
   ResetPoint reset_point_;
   ClientState state_;
-  int num_queued_fragments_;
-  int num_decoded_frames_;
-  int num_done_bitstream_buffers_;
+  size_t num_queued_fragments_;
+  size_t num_decoded_frames_;
+  size_t num_done_bitstream_buffers_;
   base::TimeTicks initialize_done_ticks_;
   GLenum texture_target_;
   VideoPixelFormat pixel_format_;
@@ -418,9 +419,9 @@
     LOG_ASSERT(1 == config_.num_in_flight_decodes);
   weak_this_ = weak_this_factory_.GetWeakPtr();
   if (config_.reset_point == MID_STREAM_RESET) {
-    EXPECT_GE(config_.reset_after_frame_num, 0)
-        << "reset_ater_frame_num_ must be >=0 "
-        << "when reset_point == MID_STREAM_RESET";
+    EXPECT_NE(config_.reset_after_frame_num, kNoMidStreamReset)
+        << "reset_ater_frame_num_ must not be kNoMidStreamReset "
+        << "when reset_point = MID_STREAM_RESET";
   } else {
     EXPECT_EQ(config_.reset_after_frame_num, kNoMidStreamReset);
   }
@@ -490,7 +491,7 @@
   LOG_ASSERT(textures_per_buffer == 1u);
   std::vector<PictureBuffer> buffers;
 
-  requested_num_of_buffers += kExtraPictureBuffers;
+  requested_num_of_buffers += static_cast<uint32_t>(kExtraPictureBuffers);
   if (pixel_format == PIXEL_FORMAT_UNKNOWN)
     pixel_format = PIXEL_FORMAT_ARGB;
 
@@ -626,8 +627,8 @@
 }
 
 void GLRenderingVDAClient::ResetDecoderAfterFlush() {
+  DCHECK_GE(remaining_play_throughs_, 1u);
   --remaining_play_throughs_;
-  DCHECK_GE(remaining_play_throughs_, 0);
   // SetState(CS_RESETTING) should be called before decoder_->Reset(), because
   // VDA can call NotifyFlushDone() from Reset().
   // TODO(johnylin): call SetState() before all decoder Flush() and Reset().
@@ -648,6 +649,7 @@
   // forward progress during a Reset().  But the VDA::Reset() API doesn't
   // guarantee this, so stop relying on it (and remove the notifications from
   // VaapiVideoDecodeAccelerator::FinishReset()).
+  LOG_ASSERT(outstanding_decodes_ != 0);
   ++num_done_bitstream_buffers_;
   --outstanding_decodes_;
 
@@ -694,7 +696,7 @@
       return;
     case START_OF_STREAM_RESET:
       reset_point_ = END_OF_STREAM_RESET;
-      for (int i = 0; i < config_.num_in_flight_decodes; ++i)
+      for (size_t i = 0; i < config_.num_in_flight_decodes; ++i)
         DecodeNextFragment();
       return;
     case END_OF_STREAM_RESET:
@@ -754,7 +756,7 @@
     return;
   }
 
-  for (int i = 0; i < config_.num_in_flight_decodes; ++i)
+  for (size_t i = 0; i < config_.num_in_flight_decodes; ++i)
     DecodeNextFragment();
   DCHECK_EQ(outstanding_decodes_, config_.num_in_flight_decodes);
 }
@@ -842,7 +844,7 @@
   if (decode_time_.size() == 0)
     return base::TimeDelta();
   std::sort(decode_time_.begin(), decode_time_.end());
-  int index = decode_time_.size() / 2;
+  size_t index = decode_time_.size() / 2;
   if (decode_time_.size() % 2 != 0)
     return decode_time_[index];
 
@@ -867,7 +869,7 @@
   // |num_concurrent_decoders| and |reset_point|. Ex: the expected number of
   // frames should be adjusted if decoder is reset in the middle of the stream.
   void UpdateTestVideoFileParams(size_t num_concurrent_decoders,
-                                 int reset_point,
+                                 ResetPoint reset_point,
                                  TestFilesVector* test_video_files);
 
   void InitializeRenderingHelper(const RenderingHelperParams& helper_params);
@@ -952,13 +954,17 @@
     if (!fields[2].empty())
       LOG_ASSERT(base::StringToInt(fields[2], &video_file->height));
     if (!fields[3].empty())
-      LOG_ASSERT(base::StringToInt(fields[3], &video_file->num_frames));
+      LOG_ASSERT(base::StringToSizeT(fields[3], &video_file->num_frames));
     if (!fields[4].empty())
-      LOG_ASSERT(base::StringToInt(fields[4], &video_file->num_fragments));
-    if (!fields[5].empty())
-      LOG_ASSERT(base::StringToInt(fields[5], &video_file->min_fps_render));
-    if (!fields[6].empty())
-      LOG_ASSERT(base::StringToInt(fields[6], &video_file->min_fps_no_render));
+      LOG_ASSERT(base::StringToSizeT(fields[4], &video_file->num_fragments));
+    if (!fields[5].empty()) {
+      std::string field(fields[5].begin(), fields[5].end());
+      LOG_ASSERT(base::StringToDouble(field, &video_file->min_fps_render));
+    }
+    if (!fields[6].empty()) {
+      std::string field(fields[5].begin(), fields[5].end());
+      LOG_ASSERT(base::StringToDouble(field, &video_file->min_fps_no_render));
+    }
     // Default to H264 baseline if no profile provided.
     int profile = static_cast<int>(H264PROFILE_BASELINE);
     if (!fields[7].empty())
@@ -977,7 +983,7 @@
 
 void VideoDecodeAcceleratorTest::UpdateTestVideoFileParams(
     size_t num_concurrent_decoders,
-    int reset_point,
+    ResetPoint reset_point,
     TestFilesVector* test_video_files) {
   for (size_t i = 0; i < test_video_files->size(); i++) {
     TestVideoFile* video_file = (*test_video_files)[i].get();
@@ -1060,9 +1066,13 @@
 // - whether the video frames are rendered as thumbnails.
 class VideoDecodeAcceleratorParamTest
     : public VideoDecodeAcceleratorTest,
-      public ::testing::WithParamInterface<
-          std::tuple<int, int, int, ResetPoint, ClientState, bool, bool>> {
-};
+      public ::testing::WithParamInterface<std::tuple<size_t,
+                                                      size_t,
+                                                      size_t,
+                                                      ResetPoint,
+                                                      ClientState,
+                                                      bool,
+                                                      bool>> {};
 
 // Wait for |note| to report a state and if it's not |expected_state| then
 // assert |client| has deleted its decoder.
@@ -1086,7 +1096,7 @@
 TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) {
   size_t num_concurrent_decoders = std::get<0>(GetParam());
   const size_t num_in_flight_decodes = std::get<1>(GetParam());
-  int num_play_throughs = std::get<2>(GetParam());
+  size_t num_play_throughs = std::get<2>(GetParam());
   const ResetPoint reset_point = std::get<3>(GetParam());
   const int delete_decoder_state = std::get<4>(GetParam());
   bool test_reuse_delay = std::get<5>(GetParam());
@@ -1119,7 +1129,7 @@
         std::make_unique<ClientStateNotification<ClientState>>();
     notes_[index] = std::move(note);
 
-    int delay_reuse_after_frame_num = std::numeric_limits<int>::max();
+    size_t delay_reuse_after_frame_num = std::numeric_limits<size_t>::max();
     if (test_reuse_delay &&
         kMaxFramesToDelayReuse * 2 < video_file->num_frames) {
       delay_reuse_after_frame_num =
@@ -1174,7 +1184,7 @@
                 static_cast<size_t>(kMinSupportedNumConcurrentDecoders));
       continue;
     }
-    for (int n = 0; n < num_play_throughs; ++n) {
+    for (size_t n = 0; n < num_play_throughs; ++n) {
       // For play-throughs other than the first, we expect initialization to
       // succeed unconditionally.
       if (n > 0) {
@@ -1226,8 +1236,8 @@
     }
     LOG(INFO) << "Decoder " << i << " fps: " << client->frames_per_second();
     if (!render_as_thumbnails) {
-      int min_fps = g_rendering_fps == 0 ? video_file->min_fps_no_render
-                                         : video_file->min_fps_render;
+      double min_fps = g_rendering_fps == 0 ? video_file->min_fps_no_render
+                                            : video_file->min_fps_render;
       if (min_fps > 0 && !test_reuse_delay)
         EXPECT_GT(client->frames_per_second(), min_fps);
     }
@@ -1277,7 +1287,8 @@
                 << filepath.value().c_str();
       int num_bytes = base::WriteFile(
           filepath, reinterpret_cast<char*>(&png[0]), png.size());
-      EXPECT_EQ(num_bytes, static_cast<int>(png.size()));
+      LOG_ASSERT(num_bytes != -1);
+      EXPECT_EQ(static_cast<size_t>(num_bytes), png.size());
       LOG(FATAL) << "Unknown thumbnails MD5: " << md5_string;
     }
   }
@@ -1601,11 +1612,11 @@
 
     if (it->first == "num_play_throughs") {
       std::string input(it->second.begin(), it->second.end());
-      LOG_ASSERT(base::StringToInt(input, &media::g_num_play_throughs));
+      LOG_ASSERT(base::StringToSizeT(input, &media::g_num_play_throughs));
       continue;
     }
     if (it->first == "fake_decoder") {
-      media::g_fake_decoder = 1;
+      media::g_fake_decoder = true;
       continue;
     }
     if (it->first == "v" || it->first == "vmodule")
diff --git a/media/gpu/windows/d3d11_cdm_proxy.cc b/media/gpu/windows/d3d11_cdm_proxy.cc
index cdd37f6..d00e580 100644
--- a/media/gpu/windows/d3d11_cdm_proxy.cc
+++ b/media/gpu/windows/d3d11_cdm_proxy.cc
@@ -12,6 +12,7 @@
 #include "media/base/callback_registry.h"
 #include "media/base/cdm_context.h"
 #include "media/base/cdm_proxy_context.h"
+#include "media/gpu/windows/d3d11_decryptor.h"
 
 namespace media {
 
@@ -112,9 +113,18 @@
   }
   CdmProxyContext* GetCdmProxyContext() override { return &cdm_proxy_context_; }
 
+  Decryptor* GetDecryptor() override {
+    if (!decryptor_)
+      decryptor_.reset(new D3D11Decryptor(&cdm_proxy_context_));
+
+    return decryptor_.get();
+  }
+
  private:
   D3D11CdmProxyContext cdm_proxy_context_;
 
+  std::unique_ptr<D3D11Decryptor> decryptor_;
+
   // TODO(rkuroiwa): Call Notify() when new usable key is available.
   ClosureRegistry new_key_callbacks_;
 
diff --git a/media/gpu/windows/d3d11_decryptor.cc b/media/gpu/windows/d3d11_decryptor.cc
new file mode 100644
index 0000000..87b064ca
--- /dev/null
+++ b/media/gpu/windows/d3d11_decryptor.cc
@@ -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.
+
+#include "media/gpu/windows/d3d11_decryptor.h"
+
+#include "base/logging.h"
+#include "media/base/decoder_buffer.h"
+
+namespace media {
+
+D3D11Decryptor::D3D11Decryptor(CdmProxyContext* cdm_proxy_context)
+    : cdm_proxy_context_(cdm_proxy_context), weak_factory_(this) {
+  DCHECK(cdm_proxy_context_);
+}
+
+D3D11Decryptor::~D3D11Decryptor() {}
+
+void D3D11Decryptor::RegisterNewKeyCB(StreamType stream_type,
+                                      const NewKeyCB& new_key_cb) {
+  // TODO(xhwang): Use RegisterNewKeyCB() on CdmContext, and remove
+  // RegisterNewKeyCB from Decryptor interface.
+  NOTREACHED();
+}
+
+void D3D11Decryptor::Decrypt(StreamType stream_type,
+                             scoped_refptr<DecoderBuffer> encrypted,
+                             const DecryptCB& decrypt_cb) {
+  // TODO(rkuroiwa): Implemented this function using |cdm_proxy_context_|.
+  NOTIMPLEMENTED();
+}
+
+void D3D11Decryptor::CancelDecrypt(StreamType stream_type) {
+  // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
+}
+
+void D3D11Decryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
+                                            const DecoderInitCB& init_cb) {
+  // D3D11Decryptor does not support audio decoding.
+  init_cb.Run(false);
+}
+
+void D3D11Decryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
+                                            const DecoderInitCB& init_cb) {
+  // D3D11Decryptor does not support video decoding.
+  init_cb.Run(false);
+}
+
+void D3D11Decryptor::DecryptAndDecodeAudio(
+    scoped_refptr<DecoderBuffer> encrypted,
+    const AudioDecodeCB& audio_decode_cb) {
+  NOTREACHED() << "D3D11Decryptor does not support audio decoding";
+}
+
+void D3D11Decryptor::DecryptAndDecodeVideo(
+    scoped_refptr<DecoderBuffer> encrypted,
+    const VideoDecodeCB& video_decode_cb) {
+  NOTREACHED() << "D3D11Decryptor does not support video decoding";
+}
+
+void D3D11Decryptor::ResetDecoder(StreamType stream_type) {
+  NOTREACHED() << "D3D11Decryptor does not support audio/video decoding";
+}
+
+void D3D11Decryptor::DeinitializeDecoder(StreamType stream_type) {
+  // D3D11Decryptor does not support audio/video decoding, but since this can be
+  // called any time after InitializeAudioDecoder/InitializeVideoDecoder,
+  // nothing to be done here.
+}
+
+}  // namespace media
diff --git a/media/gpu/windows/d3d11_decryptor.h b/media/gpu/windows/d3d11_decryptor.h
new file mode 100644
index 0000000..ef4d5ae
--- /dev/null
+++ b/media/gpu/windows/d3d11_decryptor.h
@@ -0,0 +1,50 @@
+// 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 MEDIA_GPU_WINDOWS_D3D11_DECRYPTOR_H_
+#define MEDIA_GPU_WINDOWS_D3D11_DECRYPTOR_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/decryptor.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+class CdmProxyContext;
+
+class MEDIA_GPU_EXPORT D3D11Decryptor : public Decryptor {
+ public:
+  explicit D3D11Decryptor(CdmProxyContext* cdm_proxy_context);
+  ~D3D11Decryptor() final;
+
+  // Decryptor implementation.
+  void RegisterNewKeyCB(StreamType stream_type,
+                        const NewKeyCB& key_added_cb) final;
+  void Decrypt(StreamType stream_type,
+               scoped_refptr<DecoderBuffer> encrypted,
+               const DecryptCB& decrypt_cb) final;
+  void CancelDecrypt(StreamType stream_type) final;
+  void InitializeAudioDecoder(const AudioDecoderConfig& config,
+                              const DecoderInitCB& init_cb) final;
+  void InitializeVideoDecoder(const VideoDecoderConfig& config,
+                              const DecoderInitCB& init_cb) final;
+  void DecryptAndDecodeAudio(scoped_refptr<DecoderBuffer> encrypted,
+                             const AudioDecodeCB& audio_decode_cb) final;
+  void DecryptAndDecodeVideo(scoped_refptr<DecoderBuffer> encrypted,
+                             const VideoDecodeCB& video_decode_cb) final;
+  void ResetDecoder(StreamType stream_type) final;
+  void DeinitializeDecoder(StreamType stream_type) final;
+
+ private:
+  CdmProxyContext* cdm_proxy_context_;
+
+  base::WeakPtrFactory<D3D11Decryptor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(D3D11Decryptor);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_WINDOWS_D3D11_DECRYPTOR_H_
diff --git a/media/mojo/interfaces/interface_factory.mojom b/media/mojo/interfaces/interface_factory.mojom
index 821d924..73b1e687e 100644
--- a/media/mojo/interfaces/interface_factory.mojom
+++ b/media/mojo/interfaces/interface_factory.mojom
@@ -6,6 +6,7 @@
 
 import "media/mojo/interfaces/audio_decoder.mojom";
 import "media/mojo/interfaces/cdm_proxy.mojom";
+import "media/mojo/interfaces/decryptor.mojom";
 import "media/mojo/interfaces/content_decryption_module.mojom";
 import "media/mojo/interfaces/renderer.mojom";
 import "media/mojo/interfaces/video_decoder.mojom";
@@ -60,6 +61,9 @@
   // implementation must fully validate |key_system| before creating the CDM.
   CreateCdm(string key_system, ContentDecryptionModule& cdm);
 
+  // Creates a Decryptor associated with the |cdm_id|.
+  CreateDecryptor(int32 cdm_id, Decryptor& decryptor);
+
   // Creates a CdmProxy that proxies part of CDM functionalities to a different
   // entity, e.g. hardware CDM modules. The created |cdm_proxy| must match the
   // type of the CDM, identified by |cdm_guid|.
diff --git a/media/mojo/services/interface_factory_impl.cc b/media/mojo/services/interface_factory_impl.cc
index 0caf1c6..96f59ff 100644
--- a/media/mojo/services/interface_factory_impl.cc
+++ b/media/mojo/services/interface_factory_impl.cc
@@ -11,6 +11,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/media_log.h"
+#include "media/mojo/services/mojo_decryptor_service.h"
 #include "media/mojo/services/mojo_media_client.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
@@ -167,6 +168,20 @@
 #endif  // BUILDFLAG(ENABLE_MOJO_CDM)
 }
 
+void InterfaceFactoryImpl::CreateDecryptor(int cdm_id,
+                                           mojom::DecryptorRequest request) {
+  DVLOG(2) << __func__;
+  auto mojo_decryptor_service =
+      MojoDecryptorService::Create(cdm_id, &cdm_service_context_);
+  if (!mojo_decryptor_service) {
+    DLOG(ERROR) << "MojoDecryptorService creation failed.";
+    return;
+  }
+
+  decryptor_bindings_.AddBinding(std::move(mojo_decryptor_service),
+                                 std::move(request));
+}
+
 void InterfaceFactoryImpl::CreateCdmProxy(const std::string& cdm_guid,
                                           mojom::CdmProxyRequest request) {
   DVLOG(2) << __func__;
@@ -223,6 +238,9 @@
     return false;
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
+  if (!decryptor_bindings_.empty())
+    return false;
+
   return true;
 }
 
@@ -252,6 +270,8 @@
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   cdm_proxy_bindings_.set_connection_error_handler(connection_error_cb);
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+  decryptor_bindings_.set_connection_error_handler(connection_error_cb);
 }
 
 void InterfaceFactoryImpl::OnBindingConnectionError() {
diff --git a/media/mojo/services/interface_factory_impl.h b/media/mojo/services/interface_factory_impl.h
index 05ed491..02d48e0 100644
--- a/media/mojo/services/interface_factory_impl.h
+++ b/media/mojo/services/interface_factory_impl.h
@@ -40,6 +40,7 @@
                       mojom::RendererRequest request) final;
   void CreateCdm(const std::string& key_system,
                  mojom::ContentDecryptionModuleRequest request) final;
+  void CreateDecryptor(int cdm_id, mojom::DecryptorRequest request) final;
   void CreateCdmProxy(const std::string& cdm_guid,
                       mojom::CdmProxyRequest request) final;
 
@@ -91,6 +92,8 @@
   mojo::StrongBindingSet<mojom::CdmProxy> cdm_proxy_bindings_;
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
+  mojo::StrongBindingSet<mojom::Decryptor> decryptor_bindings_;
+
   std::unique_ptr<service_manager::ServiceContextRef> connection_ref_;
   MojoMediaClient* mojo_media_client_;
   base::OnceClosure destroy_cb_;
diff --git a/media/mojo/services/media_service_unittest.cc b/media/mojo/services/media_service_unittest.cc
index 6aa13a1..4897ac8 100644
--- a/media/mojo/services/media_service_unittest.cc
+++ b/media/mojo/services/media_service_unittest.cc
@@ -17,6 +17,7 @@
 #include "media/base/mock_filters.h"
 #include "media/base/test_helpers.h"
 #include "media/mojo/buildflags.h"
+#include "media/mojo/clients/mojo_decryptor.h"
 #include "media/mojo/clients/mojo_demuxer_stream_impl.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/interfaces/constants.mojom.h"
@@ -34,8 +35,8 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-#include "media/cdm/cdm_paths.h"                    // nogncheck
-#include "media/mojo/interfaces/cdm_proxy.mojom.h"  // nogncheck
+#include "media/cdm/cdm_paths.h"  // nogncheck
+#include "media/mojo/interfaces/cdm_proxy.mojom.h"
 #endif
 
 namespace media {
@@ -43,11 +44,13 @@
 namespace {
 
 using testing::_;
+using testing::DoAll;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 using testing::NiceMock;
+using testing::SaveArg;
 using testing::StrictMock;
-using testing::WithoutArgs;
+using testing::WithArg;
 
 MATCHER_P(MatchesResult, success, "") {
   return arg->success == success;
@@ -60,6 +63,14 @@
 
 const char kSecurityOrigin[] = "https://foo.com";
 
+// Returns a trivial encrypted DecoderBuffer.
+scoped_refptr<DecoderBuffer> CreateEncryptedBuffer() {
+  scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(100));
+  encrypted_buffer->set_decrypt_config(
+      DecryptConfig::CreateCencConfig("dummy_key_id", "0123456789ABCDEF", {}));
+  return encrypted_buffer;
+}
+
 class MockCdmProxyClient : public mojom::CdmProxyClient {
  public:
   MockCdmProxyClient() = default;
@@ -119,14 +130,16 @@
   void SetUp() override {
     ServiceTest::SetUp();
 
-    media::mojom::MediaServicePtr media_service;
-    connector()->BindInterface(media::mojom::kMediaServiceName, &media_service);
-
-    service_manager::mojom::InterfaceProviderPtr interfaces;
+    service_manager::mojom::InterfaceProviderPtr host_interfaces;
     auto provider = std::make_unique<MediaInterfaceProvider>(
-        mojo::MakeRequest(&interfaces));
-    media_service->CreateInterfaceFactory(
-        mojo::MakeRequest(&interface_factory_), std::move(interfaces));
+        mojo::MakeRequest(&host_interfaces));
+
+    connector()->BindInterface(mojom::kMediaServiceName, &media_service_);
+    media_service_.set_connection_error_handler(
+        base::BindRepeating(&MediaServiceTest::MediaServiceConnectionClosed,
+                            base::Unretained(this)));
+    media_service_->CreateInterfaceFactory(
+        mojo::MakeRequest(&interface_factory_), std::move(host_interfaces));
   }
 
   MOCK_METHOD3(OnCdmInitialized,
@@ -135,23 +148,26 @@
                     mojom::DecryptorPtr decryptor));
   MOCK_METHOD0(OnCdmConnectionError, void());
 
-  void InitializeCdm(const std::string& key_system,
-                     bool expected_result,
-                     int cdm_id) {
+  // Returns the CDM ID associated with the CDM.
+  int InitializeCdm(const std::string& key_system, bool expected_result) {
     base::RunLoop run_loop;
     interface_factory_->CreateCdm(key_system, mojo::MakeRequest(&cdm_));
     cdm_.set_connection_error_handler(base::BindRepeating(
         &MediaServiceTest::OnCdmConnectionError, base::Unretained(this)));
 
-    // Have to use WithoutArgs since move-only types do not work with actions.
-    EXPECT_CALL(*this,
-                OnCdmInitialized(MatchesResult(expected_result), cdm_id, _))
-        .WillOnce(WithoutArgs(QuitLoop(&run_loop)));
+    int cdm_id = CdmContext::kInvalidCdmId;
+
+    // The last parameter mojom::DecryptorPtr is move-only and not supported by
+    // DoAll. Hence use WithArg to only extract the "int cdm_id" out and then
+    // call DoAll.
+    EXPECT_CALL(*this, OnCdmInitialized(MatchesResult(expected_result), _, _))
+        .WillOnce(WithArg<1>(DoAll(SaveArg<0>(&cdm_id), QuitLoop(&run_loop))));
     cdm_->Initialize(key_system, url::Origin::Create(GURL(kSecurityOrigin)),
                      CdmConfig(),
                      base::BindOnce(&MediaServiceTest::OnCdmInitialized,
                                     base::Unretained(this)));
     run_loop.Run();
+    return cdm_id;
   }
 
   MOCK_METHOD4(OnCdmProxyInitialized,
@@ -160,20 +176,47 @@
                     uint32_t crypto_session_id,
                     int cdm_id));
 
-  void InitializeCdmProxy(const std::string& cdm_guid) {
+  // Returns the CDM ID associated with the CdmProxy.
+  int InitializeCdmProxy(const std::string& cdm_guid) {
     base::RunLoop run_loop;
     interface_factory_->CreateCdmProxy(cdm_guid,
                                        mojo::MakeRequest(&cdm_proxy_));
 
-    EXPECT_CALL(*this, OnCdmProxyInitialized(CdmProxy::Status::kOk, _, _, _))
-        .WillOnce(QuitLoop(&run_loop));
     mojom::CdmProxyClientAssociatedPtrInfo client_ptr_info;
     cdm_proxy_client_binding_.Bind(mojo::MakeRequest(&client_ptr_info));
+    int cdm_id = CdmContext::kInvalidCdmId;
+
+    EXPECT_CALL(*this, OnCdmProxyInitialized(CdmProxy::Status::kOk, _, _, _))
+        .WillOnce(DoAll(SaveArg<3>(&cdm_id), QuitLoop(&run_loop)));
     cdm_proxy_->Initialize(
         std::move(client_ptr_info),
         base::BindOnce(&MediaServiceTest::OnCdmProxyInitialized,
                        base::Unretained(this)));
     run_loop.Run();
+    return cdm_id;
+  }
+
+  MOCK_METHOD2(OnDecrypted,
+               void(Decryptor::Status, scoped_refptr<DecoderBuffer>));
+
+  void CreateDecryptor(int cdm_id, bool expected_result) {
+    base::RunLoop run_loop;
+    mojom::DecryptorPtr decryptor_ptr;
+    interface_factory_->CreateDecryptor(cdm_id,
+                                        mojo::MakeRequest(&decryptor_ptr));
+    MojoDecryptor mojo_decryptor(std::move(decryptor_ptr));
+
+    // In the success case, there's no decryption key to decrypt the buffer so
+    // we would expect no-key.
+    auto expected_status =
+        expected_result ? Decryptor::kNoKey : Decryptor::kError;
+
+    EXPECT_CALL(*this, OnDecrypted(expected_status, _))
+        .WillOnce(QuitLoop(&run_loop));
+    mojo_decryptor.Decrypt(Decryptor::kVideo, CreateEncryptedBuffer(),
+                           base::BindRepeating(&MediaServiceTest::OnDecrypted,
+                                               base::Unretained(this)));
+    run_loop.Run();
   }
 
   MOCK_METHOD1(OnRendererInitialized, void(bool));
@@ -194,10 +237,11 @@
     mojom::RendererClientAssociatedPtrInfo client_ptr_info;
     renderer_client_binding_.Bind(mojo::MakeRequest(&client_ptr_info));
 
-    EXPECT_CALL(*this, OnRendererInitialized(expected_result))
-        .WillOnce(QuitLoop(&run_loop));
     std::vector<mojom::DemuxerStreamPtrInfo> streams;
     streams.push_back(std::move(video_stream_proxy_info));
+
+    EXPECT_CALL(*this, OnRendererInitialized(expected_result))
+        .WillOnce(QuitLoop(&run_loop));
     renderer_->Initialize(
         std::move(client_ptr_info), std::move(streams), base::nullopt,
         base::nullopt,
@@ -209,6 +253,7 @@
   MOCK_METHOD0(MediaServiceConnectionClosed, void());
 
  protected:
+  mojom::MediaServicePtr media_service_;
   mojom::InterfaceFactoryPtr interface_factory_;
   mojom::ContentDecryptionModulePtr cdm_;
   mojom::CdmProxyPtr cdm_proxy_;
@@ -232,18 +277,24 @@
 // Note: base::RunLoop::RunUntilIdle() does not work well in these tests because
 // even when the loop is idle, we may still have pending events in the pipe.
 // - If you have an InterfacePtr hosted by the service in the service process,
-//   you can use InterfacePtr::FlushForTesting().
+//   you can use InterfacePtr::FlushForTesting(). Note that this doesn't drain
+//   the task runner in the test process and doesn't cover all negative cases.
 // - If you expect a callback on an InterfacePtr call or connection error, use
 //   base::RunLoop::Run() and QuitLoop().
 
 // TODO(crbug.com/829233): Enable these tests on Android.
 #if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)
 TEST_F(MediaServiceTest, InitializeCdm_Success) {
-  InitializeCdm(kClearKeyKeySystem, true, 1);
+  InitializeCdm(kClearKeyKeySystem, true);
 }
 
 TEST_F(MediaServiceTest, InitializeCdm_InvalidKeySystem) {
-  InitializeCdm(kInvalidKeySystem, false, 0);
+  InitializeCdm(kInvalidKeySystem, false);
+}
+
+TEST_F(MediaServiceTest, Decryptor_WithCdm) {
+  int cdm_id = InitializeCdm(kClearKeyKeySystem, true);
+  CreateDecryptor(cdm_id, true);
 }
 #endif  // BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)
 
@@ -257,19 +308,66 @@
 TEST_F(MediaServiceTest, CdmProxy) {
   InitializeCdmProxy(kClearKeyCdmGuid);
 }
+
+TEST_F(MediaServiceTest, Decryptor_WithCdmProxy) {
+  int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid);
+  CreateDecryptor(cdm_id, true);
+}
+
+TEST_F(MediaServiceTest, Decryptor_WrongCdmId) {
+  int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid);
+  CreateDecryptor(cdm_id + 1, false);
+}
+
+TEST_F(MediaServiceTest, DeferredDestruction_CdmProxy) {
+  InitializeCdmProxy(kClearKeyCdmGuid);
+
+  // Disconnecting InterfaceFactory should not terminate the MediaService since
+  // there is still a CdmProxy hosted.
+  interface_factory_.reset();
+  cdm_proxy_.FlushForTesting();
+
+  // Disconnecting CdmProxy will now terminate the MediaService.
+  base::RunLoop run_loop;
+  EXPECT_CALL(*this, MediaServiceConnectionClosed())
+      .WillOnce(QuitLoop(&run_loop));
+  cdm_proxy_.reset();
+  run_loop.Run();
+}
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-TEST_F(MediaServiceTest, Lifetime) {
-  // The lifetime of the media service is controlled by the number of
-  // live InterfaceFactory impls, which are then deferred destroyed until no
-  // media components (CDM or Renderer) are hosted.
-  media::mojom::MediaServicePtr media_service;
-  connector()->BindInterface(media::mojom::kMediaServiceName, &media_service);
-  media_service.set_connection_error_handler(base::BindRepeating(
-      &MediaServiceTest::MediaServiceConnectionClosed, base::Unretained(this)));
+TEST_F(MediaServiceTest, Decryptor_WithoutCdmOrCdmProxy) {
+  // Creating decryptor without creating CDM or CdmProxy.
+  CreateDecryptor(1, false);
+}
 
+TEST_F(MediaServiceTest, Lifetime_DestroyMediaService) {
+  // Disconnecting |media_service_| doesn't terminate MediaService
+  // since |interface_factory_| is still alive. This is ensured here since
+  // MediaServiceConnectionClosed() is not called.
+  EXPECT_CALL(*this, MediaServiceConnectionClosed()).Times(0);
+  media_service_.reset();
+  interface_factory_.FlushForTesting();
+}
+
+TEST_F(MediaServiceTest, Lifetime_DestroyInterfaceFactory) {
+  // Disconnecting InterfaceFactory will now terminate the MediaService since
+  // there's no media components hosted.
+  base::RunLoop run_loop;
+  EXPECT_CALL(*this, MediaServiceConnectionClosed())
+      .WillOnce(QuitLoop(&run_loop));
+  interface_factory_.reset();
+  run_loop.Run();
+}
+
+#if (BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)) || \
+    BUILDFLAG(ENABLE_MOJO_RENDERER)
+// MediaService stays alive as long as there are InterfaceFactory impls, which
+// are then deferred destroyed until no media components (e.g. CDM or Renderer)
+// are hosted.
+TEST_F(MediaServiceTest, Lifetime) {
 #if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)
-  InitializeCdm(kClearKeyKeySystem, true, 1);
+  InitializeCdm(kClearKeyKeySystem, true);
 #endif
 
 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
@@ -290,19 +388,9 @@
   run_loop.Run();
 }
 
-#if (BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)) || \
-    BUILDFLAG(ENABLE_MOJO_RENDERER)
 TEST_F(MediaServiceTest, DeferredDestruction) {
-  // The lifetime of the media service is controlled by the number of
-  // live InterfaceFactory impls, which are then deferred destroyed until no
-  // media components (CDM or Renderer) are hosted.
-  media::mojom::MediaServicePtr media_service;
-  connector()->BindInterface(media::mojom::kMediaServiceName, &media_service);
-  media_service.set_connection_error_handler(base::BindRepeating(
-      &MediaServiceTest::MediaServiceConnectionClosed, base::Unretained(this)));
-
 #if BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)
-  InitializeCdm(kClearKeyKeySystem, true, 1);
+  InitializeCdm(kClearKeyKeySystem, true);
 #endif
 
 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
@@ -323,10 +411,10 @@
 
   // Disconnecting CDM and Renderer will now terminate the MediaService.
   base::RunLoop run_loop;
-  cdm_.reset();
-  renderer_.reset();
   EXPECT_CALL(*this, MediaServiceConnectionClosed())
       .WillOnce(QuitLoop(&run_loop));
+  cdm_.reset();
+  renderer_.reset();
   run_loop.Run();
 }
 #endif  // (BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)) ||
diff --git a/media/mojo/services/mojo_cdm_service_context.cc b/media/mojo/services/mojo_cdm_service_context.cc
index f7d62cc..49e71b5 100644
--- a/media/mojo/services/mojo_cdm_service_context.cc
+++ b/media/mojo/services/mojo_cdm_service_context.cc
@@ -38,13 +38,14 @@
 
  private:
   // CdmContext implementation.
+  Decryptor* GetDecryptor() final {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    return cdm_context_ ? cdm_context_->GetDecryptor() : nullptr;
+  }
+
   CdmProxyContext* GetCdmProxyContext() final {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-    if (!cdm_context_)
-      return nullptr;
-
-    return cdm_context_->GetCdmProxyContext();
+    return cdm_context_ ? cdm_context_->GetCdmProxyContext() : nullptr;
   }
 
   base::WeakPtr<CdmContext> cdm_context_;
diff --git a/media/mojo/services/mojo_decryptor_service.cc b/media/mojo/services/mojo_decryptor_service.cc
index c6cf6ee..8a08b90 100644
--- a/media/mojo/services/mojo_decryptor_service.cc
+++ b/media/mojo/services/mojo_decryptor_service.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/numerics/safe_conversions.h"
 #include "media/base/audio_decoder_config.h"
+#include "media/base/cdm_context.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decryptor.h"
 #include "media/base/video_decoder_config.h"
@@ -17,6 +18,7 @@
 #include "media/mojo/common/mojo_decoder_buffer_converter.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "media/mojo/interfaces/demuxer_stream.mojom.h"
+#include "media/mojo/services/mojo_cdm_service_context.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace media {
@@ -45,6 +47,29 @@
 
 }  // namespace
 
+// static
+std::unique_ptr<MojoDecryptorService> MojoDecryptorService::Create(
+    int cdm_id,
+    MojoCdmServiceContext* mojo_cdm_service_context) {
+  auto cdm_context_ref = mojo_cdm_service_context->GetCdmContextRef(cdm_id);
+  if (!cdm_context_ref) {
+    DVLOG(1) << "CdmContextRef not found for CDM ID: " << cdm_id;
+    return nullptr;
+  }
+
+  auto* cdm_context = cdm_context_ref->GetCdmContext();
+  DCHECK(cdm_context);
+
+  auto* decryptor = cdm_context->GetDecryptor();
+  if (!decryptor) {
+    DVLOG(1) << "CdmContext does not support Decryptor";
+    return nullptr;
+  }
+
+  return std::make_unique<MojoDecryptorService>(decryptor,
+                                                std::move(cdm_context_ref));
+}
+
 MojoDecryptorService::MojoDecryptorService(
     media::Decryptor* decryptor,
     std::unique_ptr<CdmContextRef> cdm_context_ref)
diff --git a/media/mojo/services/mojo_decryptor_service.h b/media/mojo/services/mojo_decryptor_service.h
index c9d25fc..17e8258 100644
--- a/media/mojo/services/mojo_decryptor_service.h
+++ b/media/mojo/services/mojo_decryptor_service.h
@@ -21,6 +21,7 @@
 namespace media {
 
 class DecoderBuffer;
+class MojoCdmServiceContext;
 class MojoDecoderBufferReader;
 class MojoDecoderBufferWriter;
 
@@ -31,6 +32,10 @@
   using StreamType = media::Decryptor::StreamType;
   using Status = media::Decryptor::Status;
 
+  static std::unique_ptr<MojoDecryptorService> Create(
+      int cdm_id,
+      MojoCdmServiceContext* mojo_cdm_service_context);
+
   // If |cdm_context_ref| is null, caller must ensure that |decryptor| outlives
   // |this|. Otherwise, |decryptor| is guaranteed to be valid as long as
   // |cdm_context_ref| is held.
@@ -101,6 +106,9 @@
   std::unique_ptr<MojoDecoderBufferWriter> decrypted_buffer_writer_;
 
   media::Decryptor* decryptor_;
+
+  // Holds the CdmContextRef to keep the CdmContext alive for the lifetime of
+  // the |decryptor_|.
   std::unique_ptr<CdmContextRef> cdm_context_ref_;
 
   base::WeakPtr<MojoDecryptorService> weak_this_;
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index f1a5128f..2efddd6d 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -147,7 +147,20 @@
 static const char kSfxLosslessHash[] = "3.03,2.86,2.99,3.31,3.57,4.06,";
 
 #if defined(OPUS_FIXED_POINT)
-// NOTE: Hashes are specific to ARM devices. x86 will not match.
+// NOTE: These hashes are specific to ARM devices, which use fixed-point Opus
+// implementation. x86 uses floating-point Opus, so x86 hashes won't match
+#if defined(ARCH_CPU_ARM64)
+static const char kOpusEndTrimmingHash_1[] =
+    "-4.57,-5.66,-6.52,-6.29,-4.37,-3.60,";
+static const char kOpusEndTrimmingHash_2[] =
+    "-11.90,-11.10,-8.26,-7.12,-7.85,-9.99,";
+static const char kOpusEndTrimmingHash_3[] =
+    "-13.30,-14.37,-13.70,-11.69,-10.20,-10.48,";
+static const char kOpusSmallCodecDelayHash_1[] =
+    "-0.48,-0.09,1.27,1.06,1.54,-0.22,";
+static const char kOpusSmallCodecDelayHash_2[] =
+    "0.29,0.15,-0.19,0.25,0.68,0.83,";
+#else
 static const char kOpusEndTrimmingHash_1[] =
     "-4.57,-5.66,-6.52,-6.30,-4.37,-3.61,";
 static const char kOpusEndTrimmingHash_2[] =
@@ -158,6 +171,8 @@
     "-0.48,-0.09,1.27,1.06,1.54,-0.22,";
 static const char kOpusSmallCodecDelayHash_2[] =
     "0.29,0.14,-0.20,0.24,0.68,0.83,";
+#endif  // defined(ARCH_CPU_ARM64)
+
 #else
 // Hash for a full playthrough of "opus-trimming-test.(webm|ogg)".
 static const char kOpusEndTrimmingHash_1[] =
diff --git a/media/video/picture.cc b/media/video/picture.cc
index dc1d48e..e7ab1db 100644
--- a/media/video/picture.cc
+++ b/media/video/picture.cc
@@ -75,7 +75,7 @@
       color_space_(color_space),
       allow_overlay_(allow_overlay),
       size_changed_(false),
-      surface_texture_(false),
+      texture_owner_(false),
       wants_promotion_hint_(false) {}
 
 Picture::Picture(const Picture& other) = default;
diff --git a/media/video/picture.h b/media/video/picture.h
index 5da84c2..0dedb51 100644
--- a/media/video/picture.h
+++ b/media/video/picture.h
@@ -116,11 +116,9 @@
 
   void set_size_changed(bool size_changed) { size_changed_ = size_changed; }
 
-  bool surface_texture() const { return surface_texture_; }
+  bool texture_owner() const { return texture_owner_; }
 
-  void set_surface_texture(bool surface_texture) {
-    surface_texture_ = surface_texture;
-  }
+  void set_texture_owner(bool texture_owner) { texture_owner_ = texture_owner; }
 
   bool wants_promotion_hint() const { return wants_promotion_hint_; }
 
@@ -135,7 +133,7 @@
   gfx::ColorSpace color_space_;
   bool allow_overlay_;
   bool size_changed_;
-  bool surface_texture_;
+  bool texture_owner_;
   bool wants_promotion_hint_;
 };
 
diff --git a/mojo/edk/BUILD.gn b/mojo/edk/BUILD.gn
index 8b6c6a19..d5beead 100644
--- a/mojo/edk/BUILD.gn
+++ b/mojo/edk/BUILD.gn
@@ -87,6 +87,12 @@
       "system/user_message_impl.h",
     ]
 
+    if (is_win) {
+      public += [ "embedder/named_platform_handle_win.h" ]
+    } else if (is_posix || is_fuchsia) {
+      public += [ "embedder/named_platform_handle_posix.h" ]
+    }
+
     sources = [
       "embedder/connection_params.cc",
       "embedder/entrypoints.cc",
diff --git a/mojo/edk/embedder/named_platform_handle.h b/mojo/edk/embedder/named_platform_handle.h
index 15ca656..e212792 100644
--- a/mojo/edk/embedder/named_platform_handle.h
+++ b/mojo/edk/embedder/named_platform_handle.h
@@ -5,47 +5,12 @@
 #ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
 #define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
 
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "mojo/edk/system/system_impl_export.h"
 
-namespace mojo {
-namespace edk {
-
-#if defined(OS_POSIX)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
-  NamedPlatformHandle() {}
-  explicit NamedPlatformHandle(const base::StringPiece& name)
-      : name(name.as_string()) {}
-
-  bool is_valid() const { return !name.empty(); }
-
-  std::string name;
-};
-#elif defined(OS_WIN)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
-  NamedPlatformHandle() {}
-  explicit NamedPlatformHandle(const base::StringPiece& name)
-      : name(base::UTF8ToUTF16(name)) {}
-
-  explicit NamedPlatformHandle(const base::StringPiece16& name)
-      : name(name.as_string()) {}
-
-  bool is_valid() const { return !name.empty(); }
-
-  base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; }
-
-  base::string16 name;
-};
-#else
-#error "Platform not yet supported."
+#if defined(OS_WIN)
+#include "mojo/edk/embedder/named_platform_handle_win.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "mojo/edk/embedder/named_platform_handle_posix.h"
 #endif
 
-}  // namespace edk
-}  // namespace mojo
-
 #endif  // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/named_platform_handle_posix.h b/mojo/edk/embedder/named_platform_handle_posix.h
new file mode 100644
index 0000000..3c7e0a1e5
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_handle_posix.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 MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
+#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
+  NamedPlatformHandle() {}
+  explicit NamedPlatformHandle(const base::StringPiece& name)
+      : name(name.as_string()) {}
+
+  bool is_valid() const { return !name.empty(); }
+
+  std::string name;
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_POSIX_H_
diff --git a/mojo/edk/embedder/named_platform_handle_win.h b/mojo/edk/embedder/named_platform_handle_win.h
new file mode 100644
index 0000000..febeb689
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_handle_win.h
@@ -0,0 +1,34 @@
+// 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 MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
+#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
+  NamedPlatformHandle() {}
+  explicit NamedPlatformHandle(const base::StringPiece& name)
+      : name(base::UTF8ToUTF16(name)) {}
+
+  explicit NamedPlatformHandle(const base::StringPiece16& name)
+      : name(name.as_string()) {}
+
+  bool is_valid() const { return !name.empty(); }
+
+  base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; }
+
+  base::string16 name;
+};
+
+}  // namespace edk
+}  // namespace mojo
+
+#endif  // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_WIN_H_
diff --git a/mojo/edk/embedder/platform_handle.cc b/mojo/edk/embedder/platform_handle.cc
index 3a1fb174..f4eb0eb3 100644
--- a/mojo/edk/embedder/platform_handle.cc
+++ b/mojo/edk/embedder/platform_handle.cc
@@ -5,16 +5,15 @@
 #include "mojo/edk/embedder/platform_handle.h"
 
 #include "build/build_config.h"
-#if defined(OS_FUCHSIA)
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_FUCHSIA)
+#include <unistd.h>
 #include <zircon/status.h>
 #include <zircon/syscalls.h>
-#endif
-#if defined(OS_POSIX)
+#elif defined(OS_POSIX)
 #include <unistd.h>
-#elif defined(OS_WIN)
-#include <windows.h>
-#else
-#error "Platform not yet supported."
 #endif
 
 #include "base/logging.h"
@@ -26,32 +25,7 @@
   if (!is_valid())
     return;
 
-#if defined(OS_FUCHSIA)
-  if (handle != ZX_HANDLE_INVALID) {
-    zx_status_t result = zx_handle_close(handle);
-    DCHECK_EQ(ZX_OK, result) << "CloseIfNecessary(zx_handle_close): "
-                             << zx_status_get_string(result);
-    handle = ZX_HANDLE_INVALID;
-  }
-  if (fd >= 0) {
-    bool success = (close(fd) == 0);
-    DPCHECK(success);
-    fd = -1;
-  }
-#elif defined(OS_POSIX)
-  if (type == Type::POSIX) {
-    bool success = (close(handle) == 0);
-    DPCHECK(success);
-    handle = -1;
-  }
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  else if (type == Type::MACH) {
-    kern_return_t rv = mach_port_deallocate(mach_task_self(), port);
-    DPCHECK(rv == KERN_SUCCESS);
-    port = MACH_PORT_NULL;
-  }
-#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   if (owning_process != base::GetCurrentProcessHandle()) {
     // This handle may have been duplicated to a new target process but not yet
     // sent there. In this case CloseHandle should NOT be called. From MSDN
@@ -84,9 +58,34 @@
   bool success = !!CloseHandle(handle);
   DPCHECK(success);
   handle = INVALID_HANDLE_VALUE;
+#elif defined(OS_FUCHSIA)
+  if (handle != ZX_HANDLE_INVALID) {
+    zx_status_t result = zx_handle_close(handle);
+    DCHECK_EQ(ZX_OK, result) << "CloseIfNecessary(zx_handle_close): "
+                             << zx_status_get_string(result);
+    handle = ZX_HANDLE_INVALID;
+  }
+  if (fd >= 0) {
+    bool success = (close(fd) == 0);
+    DPCHECK(success);
+    fd = -1;
+  }
+#elif defined(OS_POSIX)
+  if (type == Type::POSIX) {
+    bool success = (close(handle) == 0);
+    DPCHECK(success);
+    handle = -1;
+  }
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  else if (type == Type::MACH) {
+    kern_return_t rv = mach_port_deallocate(mach_task_self(), port);
+    DPCHECK(rv == KERN_SUCCESS);
+    port = MACH_PORT_NULL;
+  }
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
 #else
 #error "Platform not yet supported."
-#endif
+#endif  // defined(OS_WIN)
 }
 
 }  // namespace edk
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
index 9f6aa23..807fb6a 100644
--- a/mojo/edk/embedder/platform_handle.h
+++ b/mojo/edk/embedder/platform_handle.h
@@ -12,11 +12,11 @@
 #include <windows.h>
 
 #include "base/process/process_handle.h"
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
-#include <mach/mach.h>
 #elif defined(OS_FUCHSIA)
 #include <fdio/limits.h>
 #include <zircon/syscalls.h>
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include <mach/mach.h>
 #endif
 
 #include "base/logging.h"
@@ -24,7 +24,29 @@
 namespace mojo {
 namespace edk {
 
-#if defined(OS_FUCHSIA)
+#if defined(OS_WIN)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+  PlatformHandle() : PlatformHandle(INVALID_HANDLE_VALUE) {}
+  explicit PlatformHandle(HANDLE handle)
+      : handle(handle), owning_process(base::GetCurrentProcessHandle()) {}
+
+  void CloseIfNecessary();
+
+  bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
+
+  HANDLE handle;
+
+  // A Windows HANDLE may be duplicated to another process but not yet sent to
+  // that process. This tracks the handle's owning process and this process
+  // handle (if not null, i.e., the current process) is *owned* by this
+  // PlatformHandle.
+  base::ProcessHandle owning_process;
+
+  // A Windows HANDLE may be an unconnected named pipe. In this case, we need to
+  // wait for a connection before communicating on the pipe.
+  bool needs_connection = false;
+};
+#elif defined(OS_FUCHSIA)
 // TODO(fuchsia): Find a clean way to share this with the POSIX version.
 // |zx_handle_t| is a typedef of |int|, so we only allow PlatformHandle to be
 // created via explicit For<type>() creator functions.
@@ -94,28 +116,6 @@
   mach_port_t port = MACH_PORT_NULL;
 #endif
 };
-#elif defined(OS_WIN)
-struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
-  PlatformHandle() : PlatformHandle(INVALID_HANDLE_VALUE) {}
-  explicit PlatformHandle(HANDLE handle)
-      : handle(handle), owning_process(base::GetCurrentProcessHandle()) {}
-
-  void CloseIfNecessary();
-
-  bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
-
-  HANDLE handle;
-
-  // A Windows HANDLE may be duplicated to another process but not yet sent to
-  // that process. This tracks the handle's owning process and this process
-  // handle (if not null, i.e., the current process) is *owned* by this
-  // PlatformHandle.
-  base::ProcessHandle owning_process;
-
-  // A Windows HANDLE may be an unconnected named pipe. In this case, we need to
-  // wait for a connection before communicating on the pipe.
-  bool needs_connection = false;
-};
 #else
 #error "Platform not yet supported."
 #endif
diff --git a/mojo/edk/system/platform_wrapper_unittest.cc b/mojo/edk/system/platform_wrapper_unittest.cc
index f97be52..6b74a91 100644
--- a/mojo/edk/system/platform_wrapper_unittest.cc
+++ b/mojo/edk/system/platform_wrapper_unittest.cc
@@ -23,25 +23,25 @@
 #include <windows.h>
 #endif
 
-#if defined(OS_POSIX)
-#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
 #define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR
 #endif
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT
-#elif defined(OS_FUCHSIA)
+#if defined(OS_FUCHSIA)
 #define SHARED_BUFFER_PLATFORM_HANDLE_TYPE \
   MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE
-#else
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT
+#elif defined(OS_WIN) || defined(OS_POSIX)
 #define SHARED_BUFFER_PLATFORM_HANDLE_TYPE SIMPLE_PLATFORM_HANDLE_TYPE
 #endif
 
 uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) {
 #if defined(OS_WIN)
   return reinterpret_cast<uint64_t>(file);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return static_cast<uint64_t>(file);
 #endif
 }
@@ -49,7 +49,7 @@
 base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) {
 #if defined(OS_WIN)
   return reinterpret_cast<base::PlatformFile>(value);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return static_cast<base::PlatformFile>(value);
 #endif
 }
@@ -126,12 +126,14 @@
     MojoPlatformHandle os_buffer;
     os_buffer.struct_size = sizeof(MojoPlatformHandle);
     os_buffer.type = SHARED_BUFFER_PLATFORM_HANDLE_TYPE;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-    os_buffer.value = static_cast<uint64_t>(memory_handle.GetMemoryObject());
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
     os_buffer.value = reinterpret_cast<uint64_t>(memory_handle.GetHandle());
-#else
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+    os_buffer.value = static_cast<uint64_t>(memory_handle.GetMemoryObject());
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     os_buffer.value = static_cast<uint64_t>(memory_handle.GetHandle());
+#else
+#error Unsupported platform
 #endif
 
     MojoSharedBufferGuid mojo_guid;
@@ -179,23 +181,23 @@
 
   base::UnguessableToken guid =
       base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low);
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT, os_buffer.type);
+#if defined(OS_WIN)
+  ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE, os_buffer.type);
   base::SharedMemoryHandle memory_handle(
-      static_cast<mach_port_t>(os_buffer.value), size, guid);
+      reinterpret_cast<HANDLE>(os_buffer.value), size, guid);
 #elif defined(OS_FUCHSIA)
   ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE, os_buffer.type);
   base::SharedMemoryHandle memory_handle(
       static_cast<zx_handle_t>(os_buffer.value), size, guid);
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+  ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT, os_buffer.type);
+  base::SharedMemoryHandle memory_handle(
+      static_cast<mach_port_t>(os_buffer.value), size, guid);
 #elif defined(OS_POSIX)
   ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR, os_buffer.type);
   base::SharedMemoryHandle memory_handle(
       base::FileDescriptor(static_cast<int>(os_buffer.value), false), size,
       guid);
-#elif defined(OS_WIN)
-  ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE, os_buffer.type);
-  base::SharedMemoryHandle memory_handle(
-      reinterpret_cast<HANDLE>(os_buffer.value), size, guid);
 #endif
 
   base::SharedMemory memory(memory_handle, read_only);
diff --git a/mojo/public/cpp/system/platform_handle.h b/mojo/public/cpp/system/platform_handle.h
index 2f0cba07..5e4748c1 100644
--- a/mojo/public/cpp/system/platform_handle.h
+++ b/mojo/public/cpp/system/platform_handle.h
@@ -22,6 +22,7 @@
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/memory/writable_shared_memory_region.h"
 #include "base/process/process_handle.h"
+#include "build/build_config.h"
 #include "mojo/public/c/system/platform_handle.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/handle.h"
@@ -33,28 +34,32 @@
 
 namespace mojo {
 
-#if defined(OS_POSIX)
+#if defined(OS_WIN)
+const MojoPlatformHandleType kPlatformFileHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
+
+#elif defined(OS_FUCHSIA)
+const MojoPlatformHandleType kPlatformFileHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
+const MojoPlatformHandleType kPlatformSharedBufferHandleType =
+    MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE;
+
+#elif defined(OS_POSIX)
 const MojoPlatformHandleType kPlatformFileHandleType =
     MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
 const MojoPlatformHandleType kPlatformSharedBufferHandleType =
     MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
-#elif defined(OS_FUCHSIA)
-const MojoPlatformHandleType kPlatformSharedBufferHandleType =
-    MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE;
 #else
 const MojoPlatformHandleType kPlatformSharedBufferHandleType =
     MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
 
-#elif defined(OS_WIN)
-const MojoPlatformHandleType kPlatformFileHandleType =
-    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
-
-const MojoPlatformHandleType kPlatformSharedBufferHandleType =
-    MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
-#endif  // defined(OS_POSIX)
+#endif  // defined(OS_WIN)
 
 // Used to specify the protection status of a base::SharedMemoryHandle memory
 // handle wrapped or unwrapped by mojo::WrapSharedMemoryHandle or
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
index 176f1b89..b2ca3ea 100644
--- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -32,35 +32,30 @@
 
 {#--- Fuzzing #}
 {%- if generate_fuzzing %}
+{%-   from "fuzzing.tmpl" import generate_or_mutate %}
   {{struct.name}}.generate = function(generator_) {
     var generated = new {{struct.name}};
 {%-   for field in struct.fields %}
-{%-     if not field.kind|is_any_handle_or_interface_kind %}
-{%- from "fuzzing.tmpl" import generate_or_mutate %}
     generated.{{field.name}} = {{generate_or_mutate('generator_', 'generate', field.kind)|indent(4)}};
-{%-     endif %}
 {%-   endfor %}
     return generated;
   };
 
   {{struct.name}}.prototype.mutate = function(mutator_) {
 {%-   for field in struct.fields %}
-{%-     if not field.kind|is_any_handle_or_interface_kind %}
     if (mutator_.chooseMutateField()) {
-{%- from "fuzzing.tmpl" import generate_or_mutate %}
       this.{{field.name}} = {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}};
     }
-{%-     endif %}
 {%-   endfor %}
     return this;
   };
 
+{%-   from "fuzzing.tmpl" import get_handle_deps %}
   {{struct.name}}.prototype.getHandleDeps = function() {
     var handles = [];
 {%-   for field in struct.fields %}
 {%-     if field.kind|contains_handles_or_interfaces %}
     if (this.{{field.name}} !== null) {
-{%- from "fuzzing.tmpl" import get_handle_deps %}
       Array.prototype.push.apply(handles, {{get_handle_deps(field.kind, 'this.' ~ field.name)|indent(6)}});
     }
 {%-     endif %}
@@ -72,10 +67,10 @@
     this.setHandlesInternal_(arguments, 0);
   };
 
+{%-   from "fuzzing.tmpl" import set_handles %}
   {{struct.name}}.prototype.setHandlesInternal_ = function(handles, idx) {
 {%-   for field in struct.fields %}
 {%-     if field.kind|contains_handles_or_interfaces %}
-{%- from "fuzzing.tmpl" import set_handles %}
     {{set_handles(field.kind, 'this.' ~ field.name)|indent(4)}};
 {%-     endif %}
 {%-   endfor %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
index d00e641..499c786 100644
--- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -39,7 +39,8 @@
   this[keys[0]] = value[keys[0]];
 }
 
-{%    if generate_fuzzing %}
+{%-   if generate_fuzzing %}
+{%-     from "fuzzing.tmpl" import generate_or_mutate %}
 {{union.name}}.generate = function(generator_) {
   var generated = new {{union.name}};
   var generators = [
@@ -47,10 +48,7 @@
     {
       field: "{{field.name}}",
 
-{%-       if not field.kind|is_any_handle_or_interface_kind %}
-{%- from "fuzzing.tmpl" import generate_or_mutate %}
       generator: function() { return {{generate_or_mutate('generator_', 'generate', field.kind)|indent(6)}}; },
-{%-       endif %}
     },
 {%-     endfor %}
   ];
@@ -66,10 +64,7 @@
     {
       field: "{{field.name}}",
 
-{%-       if not field.kind|is_any_handle_or_interface_kind %}
-{%- from "fuzzing.tmpl" import generate_or_mutate %}
       mutator: function() { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; },
-{%-       endif %}
     },
 {%-     endfor %}
   ];
@@ -79,27 +74,27 @@
   return this;
 }
 
+{%-     from "fuzzing.tmpl" import get_handle_deps %}
 {{union.name}}.prototype.getHandleDeps = function() {
-{%-    for field in union.fields %}
-{%-     if field.kind|contains_handles_or_interfaces %}
+{%-     for field in union.fields %}
+{%-       if field.kind|contains_handles_or_interfaces %}
   if (this.$tag == {{union.name}}.Tags.{{field.name}}) {
-{%- from "fuzzing.tmpl" import get_handle_deps %}
     return {{get_handle_deps(field.kind, 'this.' ~ field.name)}};
   }
-{%-      endif %}
-{%-    endfor %}
+{%-       endif %}
+{%-     endfor %}
   return [];
 }
 
+{%-    from "fuzzing.tmpl" import set_handles %}
 {{union.name}}.prototype.setHandles = function() {
-{%-    for field in union.fields %}
-{%-      if field.kind|contains_handles_or_interfaces %}
+{%-      for field in union.fields %}
+{%-        if field.kind|contains_handles_or_interfaces %}
   if (this.$tag == {{union.name}}.Tags.{{field.name}}) {
-{%- from "fuzzing.tmpl" import set_handles %}
     return {{set_handles(field.kind, 'this.' ~ field.name)}};
   }
-{%-      endif %}
-{%-    endfor %}
+{%-        endif %}
+{%-      endfor %}
   return [];
 }
 {%-   endif %}
diff --git a/net/BUILD.gn b/net/BUILD.gn
index e4c934c..14dc74c 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4775,7 +4775,6 @@
     "cert/ct_log_response_parser_unittest.cc",
     "cert/ct_log_verifier_unittest.cc",
     "cert/ct_objects_extractor_unittest.cc",
-    "cert/ct_policy_enforcer_unittest.cc",
     "cert/ct_serialization_unittest.cc",
     "cert/ev_root_ca_metadata_unittest.cc",
     "cert/internal/cert_issuer_source_aia_unittest.cc",
diff --git a/net/base/file_stream_context.h b/net/base/file_stream_context.h
index 46f0ce3..d7ba39b 100644
--- a/net/base/file_stream_context.h
+++ b/net/base/file_stream_context.h
@@ -35,10 +35,11 @@
 #include "base/message_loop/message_pump_for_io.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_runner.h"
+#include "build/build_config.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/file_stream.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <errno.h>
 #endif
 
@@ -52,7 +53,7 @@
 
 #if defined(OS_WIN)
 class FileStream::Context : public base::MessagePumpForIO::IOHandler {
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 class FileStream::Context {
 #endif
  public:
@@ -65,7 +66,7 @@
   Context(base::File file, const scoped_refptr<base::TaskRunner>& task_runner);
 #if defined(OS_WIN)
   ~Context() override;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   ~Context();
 #endif
 
@@ -204,7 +205,7 @@
   // the ReadFile API.
   void ReadAsyncResult(BOOL read_file_ret, DWORD bytes_read, DWORD os_error);
 
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   // ReadFileImpl() is a simple wrapper around read() that handles EINTR
   // signals and calls RecordAndMapError() to map errno to net error codes.
   IOResult ReadFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
@@ -213,7 +214,7 @@
   // signals and calls MapSystemError() to map errno to net error codes.
   // It tries to write to completion.
   IOResult WriteFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
-#endif
+#endif  // defined(OS_WIN)
 
   base::File file_;
   bool async_in_progress_;
diff --git a/net/base/filename_util.cc b/net/base/filename_util.cc
index 1e1d51c0..16341dc 100644
--- a/net/base/filename_util.cc
+++ b/net/base/filename_util.cc
@@ -11,6 +11,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "net/base/escape.h"
 #include "net/base/filename_util_internal.h"
 #include "net/base/net_string_util.h"
@@ -49,7 +50,7 @@
   base::ReplaceSubstringsAfterOffset(
       &url_string, 0, FILE_PATH_LITERAL("?"), FILE_PATH_LITERAL("%3F"));
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
   base::ReplaceSubstringsAfterOffset(
       &url_string, 0, FILE_PATH_LITERAL("\\"), FILE_PATH_LITERAL("%5C"));
 #endif
@@ -165,7 +166,7 @@
       "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"};
 #if defined(OS_WIN)
   std::string filename_lower = base::ToLowerASCII(base::WideToUTF8(filename));
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   std::string filename_lower = base::ToLowerASCII(filename);
 #endif
 
diff --git a/net/base/filename_util_internal.cc b/net/base/filename_util_internal.cc
index 6cb28e7..bf7a6a9 100644
--- a/net/base/filename_util_internal.cc
+++ b/net/base/filename_util_internal.cc
@@ -11,6 +11,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "net/base/escape.h"
 #include "net/base/filename_util_internal.h"
 #include "net/base/mime_util.h"
@@ -88,8 +89,10 @@
     filename->resize((pos == std::string::npos) ? 0 : (pos + 1));
 #if defined(OS_WIN)
     base::TrimWhitespace(*filename, base::TRIM_TRAILING, filename);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     base::TrimWhitespaceASCII(*filename, base::TRIM_TRAILING, filename);
+#else
+#error Unsupported platform
 #endif
 
     if (filename->empty())
@@ -207,7 +210,7 @@
 #if defined(OS_WIN)
   *converted = path.value();
   return true;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   std::string component8 = path.AsUTF8Unsafe();
   return !component8.empty() &&
          base::UTF8ToUTF16(component8.c_str(), component8.size(), converted);
@@ -265,9 +268,11 @@
   replace_trailing = true;
   result_str = base::UTF8ToUTF16(filename);
   default_name_str = base::UTF8ToUTF16(default_name);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   result_str = filename;
   default_name_str = default_name;
+#else
+#error Unsupported platform
 #endif
   SanitizeGeneratedFileName(&result_str, replace_trailing);
   if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) ==
@@ -312,7 +317,7 @@
 
 #if defined(OS_WIN)
   base::FilePath generated_name(file_name);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   base::FilePath generated_name(
       base::SysWideToNativeMB(base::UTF16ToWide(file_name)));
 #endif
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index 5b40aee..f7a4c152 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -38,20 +38,22 @@
 std::wstring FilePathAsWString(const base::FilePath& path) {
 #if defined(OS_WIN)
   return path.value();
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return base::UTF8ToWide(path.value());
 #endif
 }
 base::FilePath WStringAsFilePath(const std::wstring& str) {
 #if defined(OS_WIN)
   return base::FilePath(str);
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return base::FilePath(base::WideToUTF8(str));
 #endif
 }
 
 std::string GetLocaleWarningString() {
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if defined(OS_WIN) || defined(OS_ANDROID)
+  return "";
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   // The generate filename tests can fail on certain OS_POSIX platforms when
   // LC_CTYPE is not "utf8" or "utf-8" because some of the string conversions
   // fail.
@@ -64,8 +66,6 @@
   return " this test may have failed because the current LC_CTYPE locale is "
          "not utf8 (currently set to " +
          locale + ")";
-#else
-  return "";
 #endif
 }
 
@@ -108,7 +108,7 @@
     FILE_PATH_LITERAL(" Computer"),
     FILE_PATH_LITERAL("My Computer.{a}"),
     FILE_PATH_LITERAL("My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}"),
-#if !defined(OS_WIN)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
     FILE_PATH_LITERAL("a\\a"),
 #endif
 };
@@ -195,7 +195,7 @@
      "%E9%A1%B5.doc"},
     {L"D:\\plane1\\\xD835\xDC00\xD835\xDC01.txt",  // Math alphabet "AB"
      "file:///D:/plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     {L"/foo/bar.txt", "file:///foo/bar.txt"},
     {L"/foo/BAR.txt", "file:///foo/BAR.txt"},
     {L"/C:/foo/bar.txt", "file:///C:/foo/bar.txt"},
@@ -240,7 +240,7 @@
     // %2f ('/') and %5c ('\\') are left alone by both GURL and
     // FileURLToFilePath.
     {L"C:\\foo%2f..%5cbar", "file:///C:\\foo%2f..%5cbar"},
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     {L"/c:/foo/bar.txt", "file:/c:/foo/bar.txt"},
     {L"/c:/foo/bar.txt", "file:///c:/foo/bar.txt"},
     {L"/foo/bar.txt", "file:/foo/bar.txt"},
@@ -317,7 +317,7 @@
     // Dangerous extensions
     {__LINE__, "text/html", "harmless.local", "harmless.download"},
     {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
-#else   // OS_WIN
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     // On Posix, none of the above set is particularly dangerous.
     {__LINE__, "text/html", "con.htm", "con.htm"},
     {__LINE__, "text/html", "lpt1.htm", "lpt1.htm"},
@@ -327,12 +327,12 @@
     {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
     {__LINE__, "text/html", "harmless.local", "harmless.local"},
     {__LINE__, "text/html", "harmless.lnk", "harmless.lnk"},
-#endif  // !defined(OS_WIN)
+#endif  // defined(OS_WIN)
   };
 
 #if defined(OS_WIN)
   base::FilePath base_path(L"C:\\foo");
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   base::FilePath base_path("/foo");
 #endif
 
@@ -549,7 +549,7 @@
      L"evil_"},
     {__LINE__, "", "filename=. . . . .", "", "", "binary/octet-stream",
      L"download", L"download"},
-#else  // OS_WIN
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     // Test truncation of trailing dots and spaces (non-Windows)
     {__LINE__, "", "filename=evil.exe ", "", "", "binary/octet-stream",
      L"download", L"evil.exe"},
diff --git a/net/base/iovec.h b/net/base/iovec.h
index 61e2593..50bf791 100644
--- a/net/base/iovec.h
+++ b/net/base/iovec.h
@@ -9,14 +9,14 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#include <sys/uio.h>
-#else
+#if defined(OS_WIN) || defined(OS_NACL)
 /* Structure for scatter/gather I/O.  */
 struct iovec {
   void* iov_base;  /* Pointer to data.  */
   size_t iov_len;  /* Length of data.  */
 };
-#endif  // defined(OS_POSIX) && !defined(OS_NACL)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/uio.h>
+#endif  // defined(OS_WIN) || defined(OS_NACL)
 
 #endif  // NET_BASE_IOVEC_H_
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc
index 947cd83..f535143 100644
--- a/net/base/network_interfaces_fuchsia.cc
+++ b/net/base/network_interfaces_fuchsia.cc
@@ -11,7 +11,7 @@
 
 namespace net {
 
-IPAddress NetAddressToIPAddress(const netstack::NetAddress& addr) {
+IPAddress NetAddressToIPAddress(const fuchsia::netstack::NetAddress& addr) {
   if (addr.ipv4) {
     return IPAddress(addr.ipv4->addr.data(), addr.ipv4->addr.count());
   }
@@ -22,25 +22,25 @@
 }
 
 bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
-  netstack::NetstackSyncPtr netstack =
+  fuchsia::netstack::NetstackSyncPtr netstack =
       base::fuchsia::ComponentContext::GetDefault()
-          ->ConnectToServiceSync<netstack::Netstack>();
+          ->ConnectToServiceSync<fuchsia::netstack::Netstack>();
 
-  fidl::VectorPtr<netstack::NetInterface> interfaces;
+  fidl::VectorPtr<fuchsia::netstack::NetInterface> interfaces;
   if (!netstack->GetInterfaces(&interfaces))
     return false;
 
   for (auto& interface : interfaces.get()) {
     // Check if the interface is up.
-    if (!(interface.flags & netstack::NetInterfaceFlagUp))
+    if (!(interface.flags & fuchsia::netstack::NetInterfaceFlagUp))
       continue;
 
     // Skip loopback.
-    if (interface.features & netstack::interfaceFeatureLoopback)
+    if (interface.features & fuchsia::netstack::interfaceFeatureLoopback)
       continue;
 
     NetworkChangeNotifier::ConnectionType connection_type =
-        (interface.features & netstack::interfaceFeatureWlan)
+        (interface.features & fuchsia::netstack::interfaceFeatureWlan)
             ? NetworkChangeNotifier::CONNECTION_WIFI
             : NetworkChangeNotifier::CONNECTION_UNKNOWN;
 
diff --git a/net/base/sockaddr_storage.h b/net/base/sockaddr_storage.h
index cc31231..04fa5847 100644
--- a/net/base/sockaddr_storage.h
+++ b/net/base/sockaddr_storage.h
@@ -7,12 +7,12 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX)
-#include <sys/socket.h>
-#include <sys/types.h>
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
 #include <winsock2.h>
 #include <ws2tcpip.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/socket.h>
+#include <sys/types.h>
 #endif
 
 #include "net/base/net_export.h"
diff --git a/net/base/sys_addrinfo.h b/net/base/sys_addrinfo.h
index 1799fc2d..e817ad5 100644
--- a/net/base/sys_addrinfo.h
+++ b/net/base/sys_addrinfo.h
@@ -22,7 +22,7 @@
 
 #if defined(OS_WIN)
 #include <ws2tcpip.h>
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
diff --git a/net/cert/ct_policy_enforcer.cc b/net/cert/ct_policy_enforcer.cc
index 0f77780..4749c19 100644
--- a/net/cert/ct_policy_enforcer.cc
+++ b/net/cert/ct_policy_enforcer.cc
@@ -4,302 +4,15 @@
 
 #include "net/cert/ct_policy_enforcer.h"
 
-#include <stdint.h>
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/build_time.h"
-#include "base/callback_helpers.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "net/cert/ct_known_logs.h"
 #include "net/cert/ct_policy_status.h"
-#include "net/cert/ct_verify_result.h"
-#include "net/cert/signed_certificate_timestamp.h"
-#include "net/cert/x509_certificate.h"
-#include "net/cert/x509_certificate_net_log_param.h"
-#include "net/log/net_log_capture_mode.h"
-#include "net/log/net_log_event_type.h"
-#include "net/log/net_log_parameters_callback.h"
-#include "net/log/net_log_with_source.h"
 
 namespace net {
 
-namespace {
-
-// Returns true if the current build is recent enough to ensure that
-// built-in security information (e.g. CT Logs) is fresh enough.
-// TODO(eranm): Move to base or net/base
-bool IsBuildTimely() {
-  const base::Time build_time = base::GetBuildTime();
-  // We consider built-in information to be timely for 10 weeks.
-  return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
-}
-
-// Returns a rounded-down months difference of |start| and |end|,
-// together with an indication of whether the last month was
-// a full month, because the range starts specified in the policy
-// are not consistent in terms of including the range start value.
-void RoundedDownMonthDifference(const base::Time& start,
-                                const base::Time& end,
-                                size_t* rounded_months_difference,
-                                bool* has_partial_month) {
-  DCHECK(rounded_months_difference);
-  DCHECK(has_partial_month);
-  base::Time::Exploded exploded_start;
-  base::Time::Exploded exploded_expiry;
-  start.UTCExplode(&exploded_start);
-  end.UTCExplode(&exploded_expiry);
-  if (end < start) {
-    *rounded_months_difference = 0;
-    *has_partial_month = false;
-    return;
-  }
-
-  *has_partial_month = true;
-  uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
-                        (exploded_expiry.month - exploded_start.month);
-  if (exploded_expiry.day_of_month < exploded_start.day_of_month)
-    --month_diff;
-  else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
-    *has_partial_month = false;
-
-  *rounded_months_difference = month_diff;
-}
-
-const char* CTPolicyComplianceToString(ct::CTPolicyCompliance status) {
-  switch (status) {
-    case ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
-      return "COMPLIES_VIA_SCTS";
-    case ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
-      return "NOT_ENOUGH_SCTS";
-    case ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
-      return "NOT_DIVERSE_SCTS";
-    case ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
-      return "BUILD_NOT_TIMELY";
-    case ct::CTPolicyCompliance::CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
-    case ct::CTPolicyCompliance::CT_POLICY_MAX:
-      NOTREACHED();
-      return "unknown";
-  }
-
-  NOTREACHED();
-  return "unknown";
-}
-
-std::unique_ptr<base::Value> NetLogCertComplianceCheckResultCallback(
-    X509Certificate* cert,
-    bool build_timely,
-    ct::CTPolicyCompliance compliance,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode));
-  dict->SetBoolean("build_timely", build_timely);
-  dict->SetString("ct_compliance_status",
-                  CTPolicyComplianceToString(compliance));
-  return std::move(dict);
-}
-
-// Evaluates against the policy specified at
-// https://sites.google.com/a/chromium.org/dev/Home/chromium-security/root-ca-policy/EVCTPlanMay2015edition.pdf?attredirects=0
-ct::CTPolicyCompliance CheckCTPolicyCompliance(
-    const X509Certificate& cert,
-    const ct::SCTList& verified_scts) {
-  // Cert is outside the bounds of parsable; reject it.
-  if (cert.valid_start().is_null() || cert.valid_expiry().is_null() ||
-      cert.valid_start().is_max() || cert.valid_expiry().is_max()) {
-    return ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
-  }
-
-  // Scan for the earliest SCT. This is used to determine whether to enforce
-  // log diversity requirements, as well as whether to enforce whether or not
-  // a log was qualified or pending qualification at time of issuance (in the
-  // case of embedded SCTs). It's acceptable to ignore the origin of the SCT,
-  // because SCTs delivered via OCSP/TLS extension will cover the full
-  // certificate, which necessarily will exist only after the precertificate
-  // has been logged and the actual certificate issued.
-  // Note: Here, issuance date is defined as the earliest of all SCTs, rather
-  // than the latest of embedded SCTs, in order to give CAs the benefit of
-  // the doubt in the event a log is revoked in the midst of processing
-  // a precertificate and issuing the certificate.
-  base::Time issuance_date = base::Time::Max();
-  for (const auto& sct : verified_scts) {
-    base::Time unused;
-    if (ct::IsLogDisqualified(sct->log_id, &unused))
-      continue;
-    issuance_date = std::min(sct->timestamp, issuance_date);
-  }
-
-  bool has_valid_google_sct = false;
-  bool has_valid_nongoogle_sct = false;
-  bool has_valid_embedded_sct = false;
-  bool has_valid_nonembedded_sct = false;
-  bool has_embedded_google_sct = false;
-  bool has_embedded_nongoogle_sct = false;
-  std::vector<base::StringPiece> embedded_log_ids;
-  for (const auto& sct : verified_scts) {
-    base::Time disqualification_date;
-    bool is_disqualified =
-        ct::IsLogDisqualified(sct->log_id, &disqualification_date);
-    if (is_disqualified &&
-        sct->origin != ct::SignedCertificateTimestamp::SCT_EMBEDDED) {
-      // For OCSP and TLS delivered SCTs, only SCTs that are valid at the
-      // time of check are accepted.
-      continue;
-    }
-
-    if (ct::IsLogOperatedByGoogle(sct->log_id)) {
-      has_valid_google_sct |= !is_disqualified;
-      if (sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-        has_embedded_google_sct = true;
-    } else {
-      has_valid_nongoogle_sct |= !is_disqualified;
-      if (sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED)
-        has_embedded_nongoogle_sct = true;
-    }
-    if (sct->origin != ct::SignedCertificateTimestamp::SCT_EMBEDDED) {
-      has_valid_nonembedded_sct = true;
-    } else {
-      has_valid_embedded_sct |= !is_disqualified;
-      // If the log is disqualified, it only counts towards quorum if
-      // the certificate was issued before the log was disqualified, and the
-      // SCT was obtained before the log was disqualified.
-      if (!is_disqualified || (issuance_date < disqualification_date &&
-                               sct->timestamp < disqualification_date)) {
-        embedded_log_ids.push_back(sct->log_id);
-      }
-    }
-  }
-
-  // Option 1:
-  // An SCT presented via the TLS extension OR embedded within a stapled OCSP
-  //   response is from a log qualified at time of check;
-  // AND there is at least one SCT from a Google Log that is qualified at
-  //   time of check, presented via any method;
-  // AND there is at least one SCT from a non-Google Log that is qualified
-  //   at the time of check, presented via any method.
-  //
-  // Note: Because SCTs embedded via TLS or OCSP can be updated on the fly,
-  // the issuance date is irrelevant, as any policy changes can be
-  // accomodated.
-  if (has_valid_nonembedded_sct && has_valid_google_sct &&
-      has_valid_nongoogle_sct) {
-    return ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-  // Note: If has_valid_nonembedded_sct was true, but Option 2 isn't met,
-  // then the result will be that there weren't diverse enough SCTs, as that
-  // the only other way for the conditional above to fail). Because Option 1
-  // has the diversity requirement, it's implicitly a minimum number of SCTs
-  // (specifically, 2), but that's not explicitly specified in the policy.
-
-  // Option 2:
-  // There is at least one embedded SCT from a log qualified at the time of
-  //   check ...
-  if (!has_valid_embedded_sct) {
-    // Under Option 2, there weren't enough SCTs, and potentially under
-    // Option 1, there weren't diverse enough SCTs. Try to signal the error
-    // that is most easily fixed.
-    return has_valid_nonembedded_sct
-               ? ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
-               : ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
-  }
-
-  // ... AND there is at least one embedded SCT from a Google Log once or
-  //   currently qualified;
-  // AND there is at least one embedded SCT from a non-Google Log once or
-  //   currently qualified;
-  // ...
-  //
-  // Note: This policy language is only enforced after the below issuance
-  // date, as that's when the diversity policy first came into effect for
-  // SCTs embedded in certificates.
-  // The date when diverse SCTs requirement is effective from.
-  // 2015-07-01 00:00:00 UTC.
-  const base::Time kDiverseSCTRequirementStartDate =
-      base::Time::FromInternalValue(INT64_C(13080182400000000));
-  if (issuance_date >= kDiverseSCTRequirementStartDate &&
-      !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
-    // Note: This also covers the case for non-embedded SCTs, as it's only
-    // possible to reach here if both sets are not diverse enough.
-    return ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
-  }
-
-  size_t lifetime_in_months = 0;
-  bool has_partial_month = false;
-  RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(),
-                             &lifetime_in_months, &has_partial_month);
-
-  // ... AND the certificate embeds SCTs from AT LEAST the number of logs
-  //   once or currently qualified shown in Table 1 of the CT Policy.
-  size_t num_required_embedded_scts = 5;
-  if (lifetime_in_months > 39 ||
-      (lifetime_in_months == 39 && has_partial_month)) {
-    num_required_embedded_scts = 5;
-  } else if (lifetime_in_months > 27 ||
-             (lifetime_in_months == 27 && has_partial_month)) {
-    num_required_embedded_scts = 4;
-  } else if (lifetime_in_months >= 15) {
-    num_required_embedded_scts = 3;
-  } else {
-    num_required_embedded_scts = 2;
-  }
-
-  // Sort the embedded log IDs and remove duplicates, so that only a single
-  // SCT from each log is accepted. This is to handle the case where a given
-  // log returns different SCTs for the same precertificate (which is
-  // permitted, but advised against).
-  std::sort(embedded_log_ids.begin(), embedded_log_ids.end());
-  auto sorted_end =
-      std::unique(embedded_log_ids.begin(), embedded_log_ids.end());
-  size_t num_embedded_scts =
-      std::distance(embedded_log_ids.begin(), sorted_end);
-
-  if (num_embedded_scts >= num_required_embedded_scts)
-    return ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-
-  // Under Option 2, there weren't enough SCTs, and potentially under Option
-  // 1, there weren't diverse enough SCTs. Try to signal the error that is
-  // most easily fixed.
-  return has_valid_nonembedded_sct
-             ? ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
-             : ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
-}
-
-}  // namespace
-
-ct::CTPolicyCompliance CTPolicyEnforcer::CheckCompliance(
+ct::CTPolicyCompliance DefaultCTPolicyEnforcer::CheckCompliance(
     X509Certificate* cert,
     const ct::SCTList& verified_scts,
     const NetLogWithSource& net_log) {
-  // If the build is not timely, no certificate is considered compliant
-  // with CT policy. The reasoning is that, for example, a log might
-  // have been pulled and is no longer considered valid; thus, a client
-  // needs up-to-date information about logs to consider certificates to
-  // be compliant with policy.
-  bool build_timely = IsBuildTimely();
-  ct::CTPolicyCompliance compliance;
-  if (!build_timely) {
-    compliance = ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY;
-  } else {
-    compliance = CheckCTPolicyCompliance(*cert, verified_scts);
-  }
-
-  NetLogParametersCallback net_log_callback =
-      base::Bind(&NetLogCertComplianceCheckResultCallback,
-                 base::Unretained(cert), build_timely, compliance);
-
-  net_log.AddEvent(NetLogEventType::CERT_CT_COMPLIANCE_CHECKED,
-                   net_log_callback);
-
-  return compliance;
+  return ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY;
 }
 
 }  // namespace net
diff --git a/net/cert/ct_policy_enforcer.h b/net/cert/ct_policy_enforcer.h
index fb6f4847..371d71bc 100644
--- a/net/cert/ct_policy_enforcer.h
+++ b/net/cert/ct_policy_enforcer.h
@@ -16,21 +16,19 @@
 class NetLogWithSource;
 
 namespace ct {
-
 enum class CTPolicyCompliance;
-
 }  // namespace ct
 
 class X509Certificate;
 
-using SCTList = std::vector<scoped_refptr<ct::SignedCertificateTimestamp>>;
-
-// Class for checking that a given certificate conforms to
-// Certificate Transparency-related policies.
+// Interface for checking whether or not a given certificate conforms to any
+// policies an application may have regarding Certificate Transparency.
+//
+// See //net/docs/certificate-transparency.md for more details regarding the
+// usage of CT in //net and risks that may exist when defining a CT policy.
 class NET_EXPORT CTPolicyEnforcer {
  public:
-  CTPolicyEnforcer() {}
-  virtual ~CTPolicyEnforcer() {}
+  virtual ~CTPolicyEnforcer() = default;
 
   // Returns the CT certificate policy compliance status for a given
   // certificate and collection of SCTs.
@@ -40,8 +38,23 @@
   // |applying to |cert|).
   virtual ct::CTPolicyCompliance CheckCompliance(
       X509Certificate* cert,
-      const SCTList& verified_scts,
-      const NetLogWithSource& net_log);
+      const ct::SCTList& verified_scts,
+      const NetLogWithSource& net_log) = 0;
+};
+
+// A default implementation of Certificate Transparency policies that is
+// intended for use in applications without auto-update capabilities.
+//
+// See //net/docs/certificate-transparency.md for more details.
+class NET_EXPORT DefaultCTPolicyEnforcer : public net::CTPolicyEnforcer {
+ public:
+  DefaultCTPolicyEnforcer() = default;
+  ~DefaultCTPolicyEnforcer() override = default;
+
+  ct::CTPolicyCompliance CheckCompliance(
+      X509Certificate* cert,
+      const ct::SCTList& verified_scts,
+      const NetLogWithSource& net_log) override;
 };
 
 }  // namespace net
diff --git a/net/cert/ct_verify_result.h b/net/cert/ct_verify_result.h
index 4d7ad4f..742ec65 100644
--- a/net/cert/ct_verify_result.h
+++ b/net/cert/ct_verify_result.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "net/base/net_export.h"
-#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/signed_certificate_timestamp_and_status.h"
 
 namespace net {
@@ -17,8 +16,6 @@
 
 enum class CTPolicyCompliance;
 
-typedef std::vector<scoped_refptr<SignedCertificateTimestamp> > SCTList;
-
 // Holds Signed Certificate Timestamps, depending on their verification
 // results, and information about CT policies that were applied on the
 // connection.
diff --git a/net/cert/signed_certificate_timestamp.h b/net/cert/signed_certificate_timestamp.h
index a23b708..ed0743a 100644
--- a/net/cert/signed_certificate_timestamp.h
+++ b/net/cert/signed_certificate_timestamp.h
@@ -61,7 +61,7 @@
 
 // Helper structure to represent Digitally Signed data, as described in
 // Sections 4.7 and 7.4.1.4.1 of RFC 5246.
-struct NET_EXPORT_PRIVATE DigitallySigned {
+struct NET_EXPORT DigitallySigned {
   enum HashAlgorithm {
     HASH_ALGO_NONE = 0,
     HASH_ALGO_MD5 = 1,
@@ -145,6 +145,8 @@
   DISALLOW_COPY_AND_ASSIGN(SignedCertificateTimestamp);
 };
 
+using SCTList = std::vector<scoped_refptr<ct::SignedCertificateTimestamp>>;
+
 }  // namespace ct
 
 }  // namespace net
diff --git a/net/cert/x509_certificate_net_log_param.h b/net/cert/x509_certificate_net_log_param.h
index a6721ab4..ecde73c 100644
--- a/net/cert/x509_certificate_net_log_param.h
+++ b/net/cert/x509_certificate_net_log_param.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "net/base/net_export.h"
+
 namespace base {
 class Value;
 }
@@ -17,7 +19,7 @@
 class X509Certificate;
 
 // Creates NetLog parameter to describe an X509Certificate.
-std::unique_ptr<base::Value> NetLogX509CertificateCallback(
+NET_EXPORT std::unique_ptr<base::Value> NetLogX509CertificateCallback(
     const X509Certificate* certificate,
     NetLogCaptureMode capture_mode);
 
diff --git a/net/cert_net/cert_net_fetcher_impl_unittest.cc b/net/cert_net/cert_net_fetcher_impl_unittest.cc
index fbf112b..84ef95f88 100644
--- a/net/cert_net/cert_net_fetcher_impl_unittest.cc
+++ b/net/cert_net/cert_net_fetcher_impl_unittest.cc
@@ -53,7 +53,8 @@
         std::make_unique<TransportSecurityState>());
     storage_.set_cert_transparency_verifier(
         std::make_unique<MultiLogCTVerifier>());
-    storage_.set_ct_policy_enforcer(std::make_unique<CTPolicyEnforcer>());
+    storage_.set_ct_policy_enforcer(
+        std::make_unique<DefaultCTPolicyEnforcer>());
     storage_.set_proxy_resolution_service(ProxyResolutionService::CreateFixed(
         ProxyConfigWithAnnotation(no_proxy, TRAFFIC_ANNOTATION_FOR_TESTS)));
     storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
diff --git a/net/disk_cache/cache_util.cc b/net/disk_cache/cache_util.cc
index fa18675..8cd39bf 100644
--- a/net/disk_cache/cache_util.cc
+++ b/net/disk_cache/cache_util.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 
 namespace {
 
@@ -117,11 +118,11 @@
 
   base::FilePath path = current_path.DirName();
   base::FilePath name = current_path.BaseName();
-#if defined(OS_POSIX)
-  std::string name_str = name.value();
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   // We created this file so it should only contain ASCII.
   std::string name_str = base::UTF16ToASCII(name.value());
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  std::string name_str = name.value();
 #endif
 
   base::FilePath to_delete = GetTempCacheName(path, name_str);
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 2b68396..a238ecb 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -6,11 +6,8 @@
 
 #if defined(OS_WIN)
 #include <Winsock2.h>
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <netdb.h>
-#endif
-
-#if defined(OS_POSIX)
 #include <netinet/in.h>
 #if !defined(OS_NACL)
 #include <net/if.h>
@@ -18,7 +15,7 @@
 #include <ifaddrs.h>
 #endif  // !defined(OS_ANDROID)
 #endif  // !defined(OS_NACL)
-#endif  // defined(OS_POSIX)
+#endif  // defined(OS_WIN)
 
 #include <cmath>
 #include <memory>
@@ -48,6 +45,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "net/base/address_family.h"
 #include "net/base/address_list.h"
 #include "net/base/host_port_pair.h"
@@ -124,7 +122,7 @@
     "Net.OSErrorsForGetAddrinfo_Mac";
 #elif defined(OS_LINUX)
     "Net.OSErrorsForGetAddrinfo_Linux";
-#else
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     "Net.OSErrorsForGetAddrinfo";
 #endif
 
@@ -133,7 +131,21 @@
 // a histogram.
 std::vector<int> GetAllGetAddrinfoOSErrors() {
   int os_errors[] = {
-#if defined(OS_POSIX)
+#if defined(OS_WIN)
+    // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx
+    WSA_NOT_ENOUGH_MEMORY,
+    WSAEAFNOSUPPORT,
+    WSAEINVAL,
+    WSAESOCKTNOSUPPORT,
+    WSAHOST_NOT_FOUND,
+    WSANO_DATA,
+    WSANO_RECOVERY,
+    WSANOTINITIALISED,
+    WSATRY_AGAIN,
+    WSATYPE_NOT_FOUND,
+    // The following are not in doc, but might be to appearing in results :-(.
+    WSA_INVALID_HANDLE,
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #if !defined(OS_FREEBSD)
 #if !defined(OS_ANDROID)
     // EAI_ADDRFAMILY has been declared obsolete in Android's and
@@ -152,20 +164,6 @@
     EAI_SERVICE,
     EAI_SOCKTYPE,
     EAI_SYSTEM,
-#elif defined(OS_WIN)
-    // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx
-    WSA_NOT_ENOUGH_MEMORY,
-    WSAEAFNOSUPPORT,
-    WSAEINVAL,
-    WSAESOCKTNOSUPPORT,
-    WSAHOST_NOT_FOUND,
-    WSANO_DATA,
-    WSANO_RECOVERY,
-    WSANOTINITIALISED,
-    WSATRY_AGAIN,
-    WSATYPE_NOT_FOUND,
-    // The following are not in doc, but might be to appearing in results :-(.
-    WSA_INVALID_HANDLE,
 #endif
   };
 
@@ -333,12 +331,16 @@
 // Also returns false if it cannot determine this.
 bool HaveOnlyLoopbackAddresses() {
   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::WILL_BLOCK);
-#if defined(OS_ANDROID)
+#if defined(OS_WIN)
+  // TODO(wtc): implement with the GetAdaptersAddresses function.
+  NOTIMPLEMENTED();
+  return false;
+#elif defined(OS_ANDROID)
   return android::HaveOnlyLoopbackAddresses();
 #elif defined(OS_NACL)
   NOTIMPLEMENTED();
   return false;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   struct ifaddrs* interface_addr = NULL;
   int rv = getifaddrs(&interface_addr);
   if (rv != 0) {
@@ -373,13 +375,6 @@
   }
   freeifaddrs(interface_addr);
   return result;
-#elif defined(OS_WIN)
-  // TODO(wtc): implement with the GetAdaptersAddresses function.
-  NOTIMPLEMENTED();
-  return false;
-#else
-  NOTIMPLEMENTED();
-  return false;
 #endif  // defined(various platforms)
 }
 
@@ -397,9 +392,7 @@
 
   if (os_error) {
     dict->SetInteger("os_error", os_error);
-#if defined(OS_POSIX)
-    dict->SetString("os_error_string", gai_strerror(os_error));
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
     // Map the error code to a human-readable string.
     LPWSTR error_string = nullptr;
     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
@@ -411,6 +404,8 @@
                   0);  // Arguments (unused).
     dict->SetString("os_error_string", base::WideToUTF8(error_string));
     LocalFree(error_string);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+    dict->SetString("os_error_string", gai_strerror(os_error));
 #endif
   }
 
@@ -1999,12 +1994,14 @@
 #if defined(OS_WIN)
   EnsureWinsockInit();
 #endif
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if (defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)) || \
+    defined(OS_FUCHSIA)
   RunLoopbackProbeJob();
 #endif
   NetworkChangeNotifier::AddIPAddressObserver(this);
   NetworkChangeNotifier::AddConnectionTypeObserver(this);
   NetworkChangeNotifier::AddDNSObserver(this);
+// TODO(crbug/836416): Remove OS_FUCHSIA here.
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \
     !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
   EnsureDnsReloaderInit();
@@ -2525,7 +2522,8 @@
   probe_weak_ptr_factory_.InvalidateWeakPtrs();
   if (cache_.get())
     cache_->OnNetworkChange();
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if (defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)) || \
+    defined(OS_FUCHSIA)
   RunLoopbackProbeJob();
 #endif
   AbortAllInProgressJobs();
diff --git a/net/http/http_auth_preferences.cc b/net/http/http_auth_preferences.cc
index 10815d1..9b532d1e 100644
--- a/net/http/http_auth_preferences.cc
+++ b/net/http/http_auth_preferences.cc
@@ -17,7 +17,7 @@
 HttpAuthPreferences::HttpAuthPreferences()
     : HttpAuthPreferences(std::vector<std::string>()) {}
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
 HttpAuthPreferences::HttpAuthPreferences(
     const std::vector<std::string>& auth_schemes)
     : HttpAuthPreferences(auth_schemes,
@@ -35,7 +35,7 @@
 #if defined(OS_CHROMEOS)
     ,
     bool allow_gssapi_library_load
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
     ,
     const std::string& gssapi_library_name
 #endif
@@ -48,7 +48,7 @@
 #endif
 #if defined(OS_CHROMEOS)
       allow_gssapi_library_load_(allow_gssapi_library_load),
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
       gssapi_library_name_(gssapi_library_name),
 #endif
       security_manager_(URLSecurityManager::Create()) {
@@ -68,7 +68,7 @@
   return negotiate_enable_port_;
 }
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
 bool HttpAuthPreferences::NtlmV2Enabled() const {
   return ntlm_v2_enabled_;
 }
@@ -82,7 +82,7 @@
 bool HttpAuthPreferences::AllowGssapiLibraryLoad() const {
   return allow_gssapi_library_load_;
 }
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 std::string HttpAuthPreferences::GssapiLibraryName() const {
   return gssapi_library_name_;
 }
diff --git a/net/http/http_auth_preferences.h b/net/http/http_auth_preferences.h
index 1802765f..372e7e8 100644
--- a/net/http/http_auth_preferences.h
+++ b/net/http/http_auth_preferences.h
@@ -27,7 +27,7 @@
   // |allow_gssapi_library_load| set to true.
   HttpAuthPreferences();
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
   // Simplified ctor with empty |gssapi_library_name| and
   // |allow_gssapi_library_load| set to true.
   // On platforms where this is not available, the ctor below is already
@@ -39,7 +39,7 @@
 #if defined(OS_CHROMEOS)
                       ,
                       bool allow_gssapi_library_load
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
                       ,
                       const std::string& gssapi_library_name
 #endif
@@ -49,14 +49,14 @@
   virtual bool IsSupportedScheme(const std::string& scheme) const;
   virtual bool NegotiateDisableCnameLookup() const;
   virtual bool NegotiateEnablePort() const;
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
   virtual bool NtlmV2Enabled() const;
 #endif
 #if defined(OS_ANDROID)
   virtual std::string AuthAndroidNegotiateAccountType() const;
 #elif defined(OS_CHROMEOS)
   virtual bool AllowGssapiLibraryLoad() const;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   virtual std::string GssapiLibraryName() const;
 #endif
   virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const;
@@ -70,7 +70,7 @@
     negotiate_enable_port_ = negotiate_enable_port;
   }
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
   void set_ntlm_v2_enabled(bool ntlm_v2_enabled) {
     ntlm_v2_enabled_ = ntlm_v2_enabled;
   }
@@ -94,7 +94,7 @@
   bool negotiate_disable_cname_lookup_;
   bool negotiate_enable_port_;
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
   bool ntlm_v2_enabled_;
 #endif
 
@@ -102,7 +102,7 @@
   std::string auth_android_negotiate_account_type_;
 #elif defined(OS_CHROMEOS)
   const bool allow_gssapi_library_load_;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   // GSSAPI library name cannot change after startup, since changing it
   // requires unloading the existing GSSAPI library, which could cause all
   // sorts of problems for, for example, active Negotiate transactions.
diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc
index c73d5ba..eaf1502 100644
--- a/net/http/http_network_layer_unittest.cc
+++ b/net/http/http_network_layer_unittest.cc
@@ -267,7 +267,7 @@
   std::unique_ptr<CertVerifier> cert_verifier_;
   std::unique_ptr<TransportSecurityState> transport_security_state_;
   MultiLogCTVerifier ct_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
   const scoped_refptr<SSLConfigService> ssl_config_service_;
   std::unique_ptr<HttpNetworkSession> network_session_;
diff --git a/net/http/http_network_transaction_ssl_unittest.cc b/net/http/http_network_transaction_ssl_unittest.cc
index fe5c4fb..9d678d7 100644
--- a/net/http/http_network_transaction_ssl_unittest.cc
+++ b/net/http/http_network_transaction_ssl_unittest.cc
@@ -99,7 +99,7 @@
   MockCertVerifier cert_verifier_;
   TransportSecurityState transport_security_state_;
   MultiLogCTVerifier ct_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   HttpNetworkSession::Context session_context_;
   std::vector<std::unique_ptr<HttpRequestInfo>> request_info_vector_;
 };
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc
index 0131c08..8e2568c 100644
--- a/net/http/http_proxy_client_socket_wrapper_unittest.cc
+++ b/net/http/http_proxy_client_socket_wrapper_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "build/build_config.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/do_nothing_ct_verifier.h"
 #include "net/cert/mock_cert_verifier.h"
 #include "net/dns/mock_host_resolver.h"
@@ -210,7 +211,7 @@
   MockTaggingClientSocketFactory socket_factory_;
   HttpServerPropertiesImpl http_server_properties_;
   std::unique_ptr<MockCertVerifier> cert_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   std::unique_ptr<ChannelIDService> channel_id_service_;
   TransportSecurityState transport_security_state_;
   std::unique_ptr<DoNothingCTVerifier> cert_transparency_verifier_;
diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc
index 0a7d2b1..4525ca2 100644
--- a/net/http/http_response_body_drainer_unittest.cc
+++ b/net/http/http_response_body_drainer_unittest.cc
@@ -265,7 +265,7 @@
   MockCertVerifier cert_verifier_;
   TransportSecurityState transport_security_state_;
   MultiLogCTVerifier ct_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   const std::unique_ptr<HttpNetworkSession> session_;
   CloseResultWaiter result_waiter_;
   MockHttpStream* const mock_stream_;  // Owned by |drainer_|.
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index cd4ced5..00fca6d 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -831,7 +831,7 @@
     session_context.transport_security_state = &transport_security_state;
     MultiLogCTVerifier ct_verifier;
     session_context.cert_transparency_verifier = &ct_verifier;
-    CTPolicyEnforcer ct_policy_enforcer;
+    DefaultCTPolicyEnforcer ct_policy_enforcer;
     session_context.ct_policy_enforcer = &ct_policy_enforcer;
     session_context.proxy_resolution_service = proxy_resolution_service.get();
     session_context.ssl_config_service = ssl_config_service.get();
@@ -997,7 +997,7 @@
       TestProxyDelegate test_proxy_delegate;
       HttpServerPropertiesImpl http_server_properties;
       MockCertVerifier cert_verifier;
-      CTPolicyEnforcer ct_policy_enforcer;
+      DefaultCTPolicyEnforcer ct_policy_enforcer;
       MultiLogCTVerifier ct_verifier;
       scoped_refptr<SSLConfigServiceDefaults> ssl_config_service(
           new SSLConfigServiceDefaults);
@@ -1112,7 +1112,7 @@
     TestProxyDelegate test_proxy_delegate;
     HttpServerPropertiesImpl http_server_properties;
     MockCertVerifier cert_verifier;
-    CTPolicyEnforcer ct_policy_enforcer;
+    DefaultCTPolicyEnforcer ct_policy_enforcer;
     MultiLogCTVerifier ct_verifier;
 
     scoped_refptr<SSLConfigServiceDefaults> ssl_config_service(
@@ -2468,7 +2468,7 @@
   HttpServerPropertiesImpl http_server_properties_;
   TransportSecurityState transport_security_state_;
   MultiLogCTVerifier ct_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   MockHostResolver host_resolver_;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
   scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 44ba3ab..aa1a289 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -30736,7 +30736,6 @@
     { "name": "mireillewendling.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moojp.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "momstableonline.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "motosikletevi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "miguelmartinez.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "monteurzimmerfrei.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "moreserviceleads.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -40857,7 +40856,6 @@
     { "name": "wtw.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wyu.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xianguocy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "xtreme-servers.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xuntaosms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yallamotor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yhwj.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41319,7 +41317,6 @@
     { "name": "semaphore-studios.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sergefonville.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "servers4all.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sh0rt.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shellj.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shellshock.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shoxmusic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46055,7 +46052,6 @@
     { "name": "caryefurd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "catalogobiblioteca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cb-crochet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "cbmusa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cd5k.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cdasenegal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cedricmartineau.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49385,7 +49381,6 @@
     { "name": "beimchristoph.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "berhampore-gateway.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bitcoinfo.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "brctec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brplusdigital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bsg.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bsgamanet.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49637,6 +49632,810 @@
     { "name": "zhuji5.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zx1168.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zx2268.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "00dani.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "101.qa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "123writings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "12thmanrising.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "22digital.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24dian30.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24pcr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "288da.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2rsc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2rsc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "365propertybuyer.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "51845.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "73info.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8hrs.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8shequapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "97bros.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9won.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a1moldsolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abilityone.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ableprop.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "accessoirescheveuxchic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acorntreecare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acoustics.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acrepairgeorgetown.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acrepairhutto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acrepairroundrocktx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acrolife.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adsl2meg.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aei.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aereco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "affping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agingstats.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aginion.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "agliamici.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aide-admin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "akasha.world", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "akustik.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alasdelalma.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aldo-vandini.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alesia-formation.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alikulov.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alloverthehill.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "allstarquilts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aluoblog.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aluoblog.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amisderodin.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andre-lategan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "angelremigene.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anime-tip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anthonyloop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "antifraud.net.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arrowfastener.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "artfullyelegant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asoftwareco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aspectcontext.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "assistenzaferrodastiro.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "assistenzafrigorifero.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "athenadynamics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atimbertownservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atlasdev.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "atsoftware.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aucielrose.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "audioonly.stream", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "audiophix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "autocontrol.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "awningsatlantaga.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ayanomimi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "azzurrapelletterie.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bahnhelden.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bamahammer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bandar303.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "barnel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "barpodsosnami.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "basementfinishingohio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "batch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bbj.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bednar.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "betterweb.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bezr.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "biehlsoft.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bifrost.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bigshort.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bijouxcherie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "binarization.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blockchainced.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluerootsmarketing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluestardiabetes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bokka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boltbeat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bonsaimedia.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bookingentertainment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boonehenry.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boote.wien", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bravebaby.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "briefassistant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brouwerijkoelit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buileo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "buradangonder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "businessplanexperts.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8601.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8602.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8603.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8604.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8605.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwin8606.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cachethome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cachetur.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cafedupont.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cajio.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "callmereda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "capacityproject.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "captivationtheory.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "carbon-project.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "celtadigital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "century-group.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cernakova.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "certevia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cftc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "challstrom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "charlespitonltd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "charlotteswimmingpoolbuilder.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chartkick.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cheapcaribbean.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chenkun.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "childstats.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "christopherandcharlotte.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ci-suite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cinay.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cineplex.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cintactimber.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clawhammer.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clod-hacking.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cnet-hosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "co-founder-stuttgart.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "codeandpeace.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "codeproxy.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "codestep.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cognitip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "collegenavigator.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "collegeprospectsofcentralindiana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comercialtpv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "comptrollerofthecurrency.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "concertsto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "concreterepairatlanta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "connexas.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "convertimg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "convoitises.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "corder.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "corpoflow.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cosciamoos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cougarsland.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cr0nus.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crawlspaceandbasementsolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cryptoegg.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cryptofrog.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cryptopro.shop", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crystalgrid.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cultureelbeleggen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "curarnosensalud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "custerweb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cvps.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cybit.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cygu.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cykelbanor.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d-macindustries.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d00r.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d3xx3r.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dak.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "damongant.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "damonline.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danielhinterlechner.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "datalife.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dayofdays.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dearktiel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deckbuilderamerica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deelmijnreis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "defcongroups.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "defendtheweb.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dekkercreativedesign.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "delandalucia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "derbuntering.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dermscc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deyute.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "didierghez.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "die-pizzabaeckerei.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digital-liberal.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digital.govt.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diplona.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "directelectricalltd.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "discoveryaima.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "disinfestazioneblatte.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "disinfestazioni.bari.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dlz149.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dogadayiz.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doggedbyirs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dong8.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doomus.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dougley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dpangerl.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dreamrae.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drei01.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dsyunmall.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dunkle-seite.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "e-speak24.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "easyweenies.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eat-mine.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ecir.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ecir.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ecofac-bs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edenvaleplumber24-7.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edh.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "egamespw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "einser.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "electrician-umhlangaridge.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eliasojala.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elibom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ell-net.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ellencorddry.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emeraldcityswagger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "empherino.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "energy-initiative.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "engiedev.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ensurtec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "envoie.moi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "envoyez.moi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eru.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "escuelabiblica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "euteamo.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eutotal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eventplace.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "everydaygary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evoco.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "extremeservicesandrestoration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fabiankoeppen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fabriziorocca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fackovcova.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fackovcova.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fackovcova.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fackovec.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fackovec.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "falling.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "feedfall.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ferienhausprovence.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ferm-rotterdam.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ffh.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fileon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "filmesonline.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fiskestang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.lu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcor.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fleetcorcards.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flirtee.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flirtos.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "floj.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flosserver.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flurp.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fmc.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "folk.as", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foreveryoung.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "francois-gaillard.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freaksports.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fredericcote.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frei.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckobr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckobr.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckobr.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckobr.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckobr.su", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fuckonthefirst.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fur.red", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "future-moves.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fyol.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "g5.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gameblabla.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gamesaviour.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gamesided.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gamilab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gamilab.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ganggalbichler.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "garrowmediallc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gcfadvisors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "geba-online.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "geckler-ee.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "genesysmi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gentooblog.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "get.how", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gilium.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "giraffeduck.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "glevolution.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "globcoin.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "golf18staging.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "govisitcostarica.co.cr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "govisitcostarica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gozenhost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gpgscoins.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "graf-igor.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "graft.observer", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greatergoodoffers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "greatfire.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "groenteclub.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gunceloyunhileleri.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guts.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "habbig.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hamiltonlinen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hanfverband-erfurt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hang333.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "haoqi.men", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hardloopfysio.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hausarztpraxis-linn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "headshopinternational.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "healthit.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hearty.org.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heatingandairconditioningdallastx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heiwa-valve.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hejianpeng.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hentaiz.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "highperformancehvac.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hollerau.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "homegreenmark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hopla.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "housese.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hryniewski.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hstspreload.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "humorce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hunterkehoe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "huurwoordenaar.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hvmk.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hybula.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hybula.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hydai.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hyperporn.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iam.soy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ifreetion.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "illerzell.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imaginary.stream", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imobile3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infocon.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infranium.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infranium.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infranium.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infranium.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inox.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "inoxio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ins-kreativ.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insecure.org.je", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insrt.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "insurgentsmustdie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "interaktiva.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "interiortradingco.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iofort.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ionovia.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ipadportfolioapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iphoneportfolioapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iren.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "isabelaflores.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ishet.al", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ispfontela.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "issa.org.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "isthedoorlocked.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ivre.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iwanttoliveinabunker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jaarvistech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jabbers.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jakeslab.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jamesl.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jeda.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jenniferengerwingaantrouwen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jeremypaul.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jiazhao.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jimbiproducts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jmcataffo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jtl-software.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "junias-fenske.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "juusujanar.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jydemarked.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kabarlinux.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kagitreklam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kanetix.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kanzlei-oehler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "karupp-did.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kastorsky.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kellyssportsbarandgrill.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kelvinfichter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kindlezs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kirkify.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "klantenadvies.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "klingsundet.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "koerperkult.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kolcsey.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kotonoha.cafe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kovuthehusky.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kryha.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ksk-agentur.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kuops.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lacetsfun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ladenzeile.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ladenzeile.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lambertshealthcare.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "langkawitrip.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "langzijn.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laohei.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laplacesicherheit.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lars.moi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lca.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lcars-sv.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lcy.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lcy.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lcybox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "legalisepeacebloom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lemonthy.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lemonthy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lenaneva.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "letsdebug.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leumi-how-to.co.il", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lhakustik.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "libertins.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "life-emotions.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "littlericket.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "livela.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lixiaojiang.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "liz.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lizhi123.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lnbeauty.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "loafhead.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lobstr.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "localethereum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "logicne-hise.si", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "logoglo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lotro-wiki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lshiy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ludogue.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lunapatch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luyckx.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lxd.pm", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "majkl.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "majkl.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "majkl578.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "malermeister-haussmann.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "malwareverse.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mandanudes.ae", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marco-hegenberg.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marseillekiteclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "masteragenasia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mastersquirrel.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mathsweek.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mathsweek.org.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "matmessages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mbaasy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mbanq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mede-handover.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medmarkt24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mega-aukcion.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meintragebaby.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "melissaauclaire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "metro-lawn-care.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michaeliscorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michaelpelletterie.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michaelschmidt.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mikehilldesign.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mikywow.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mindcell.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mirgleich.dnshome.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mirjamderijk.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "missyou.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mkchandler.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mkkkrc.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mobila-chisinau.md", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "momento.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monkieteel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monkieteel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "montemanik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "montessori.edu.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moot-info.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "motd.today", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mousemessages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mozektevidi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mrburtbox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mtane0412.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mtlconcerts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "multizone.games", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mxn8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "my4g.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mybagofcoffee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mycrypto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mydrone.services", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mydroneservices.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mydroneservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myetherwallet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myipaddr.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrepubiic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrepublic.com.kh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrepublic.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrepublic.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "myrepublicinternet.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mywallets.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "n-a.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "n0s.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nami.bo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nami.trade", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "narakenkoland.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "naturblogg.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "naturesbest.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nea.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nems.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nesbase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "net-rencontre.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newtonproject.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nexthop.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nflmocks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nhw.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nic.goog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nic.how", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nic.soy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nic.xn--q9jyb4c", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "niki.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "niklas.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nmgb.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nmgb.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nobitakun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nodesec.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "northerngate.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nosecrets.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "notora.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "noxi.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nrc-gateway.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nur.berlin", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nvl-game.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nwr-waffenbuch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nya.as", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nyan.stream", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "oakesfam.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "objectif-leger.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "obono.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "odpikedoslike.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ogyaa.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "okotoksbeach.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "olgun.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ollieowlsblog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "onazikgu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ongea.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "openreview.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "orgatech-gmbh.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "originalsport.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "orionfinancialservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ovix.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paintcolorsbysue.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "palariviera.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parodesigns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "partiwatch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pashminacachemire.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pci-dss.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pcidss.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "penslabyrinth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "performancehealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "perthtrains.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "peters.consulting", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "philippinedroneassociation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "philna.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pinkbikecycle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pintoselectrician.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pjo.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "placebet.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "plumplat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pornhubhd.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "powerball.shop", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "powermatic7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pplsvc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prematureacceleration.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "preposted.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "princessefoulard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prismacloud.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pro-image.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "projet-fly.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "promo-computers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "promohulp.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "protege.moi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "psychologie-hofner.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "purbd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qazcloud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qoml.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qoor.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quic.stream", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qx.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rahadiana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "raptorsrapture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rawdutch.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rcd.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rdwh.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "redgoose.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "redwaterhost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "referdell.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reflectores.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "registerex.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rehabmail.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reichardt-home.goip.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reiki-france.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reimaginebelonging.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rekisuta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rencontres-erotiques.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "retetop95.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "retrovideospiele.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "revisit.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rheinturm.nrw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "riederle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robertbln.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robertopazeller.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "roeleveld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "romantica-hotel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "romar-bos.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "roms.fun", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rothe.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "route-wird-berechnet.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rritv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "s0laris.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sacprincesse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "safestore.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sagargandecha.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "samdev.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sammenlignakasser.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "satal.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sayori.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sbsbaits.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schillers-friedberg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schneider-electric.tg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "school-register.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schur-it.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sclns.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scripter.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sec.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "secteer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "seosec.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "septicrepairspecialists.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sheet.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sherrikehoetherapy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shivamber.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shopalike.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shybynature.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "si2b.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sictame-tigf.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "silent-clean.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simonshine.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sircon.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sisgopro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sistov.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "skrivande.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smit.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smithchow.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "smmcab.website", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sortingwizard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "soulcraft.bz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spar-ni.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spirit-hunters-germany.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spitfiredialers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sportakrobatik.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sportsjaw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sppin.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stadtbuecherei-bad-wurzach.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "standard.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stefan-rothe.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stefancosma.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "steffentreeservice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stellarguard.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stickertuningfetzt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stolin.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "straightedgebarbers.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stromberger.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strommenhome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "studioproapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "subsistence.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "successwithflora.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "succubus.xxx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "suessdeko.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "superaficionados.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "switcheo.exchange", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "switcheo.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "symlnk.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "talking12.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "targetexecutivesearch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tcmwellnessclinic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tea.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "techtrackerpro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tekniskakustik.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "telegramdr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tendance-et-accessoires.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "terabyteit.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teusink.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "the420vape.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thebakery2go.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thesmokingcuban.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "threatmarket.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tildes.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "timklefisch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tokens.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toniharant.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "topekafoundationpros.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "toushi-shakkin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trauer-beileid.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trea98.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trillian.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trtruijens.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ts3-dns.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tunnelventilation.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "twitchplaysleaderboard.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ueberdosis.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "umsolugar.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unityconsciousnessbooks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unsereins.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uprouteyou.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usatomotori.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "usuluddin.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uteam.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "utilityreport.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "v-spin.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vancityconcerts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vec.ac.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "venje.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ver-ooginoog.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verasani.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verizonconnect.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vi.photo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vicjuwelen-annelore.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "videosdiversosdatv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "viralsouls.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vistacampus.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "visualdrone.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "viyf.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vkb-remont.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vmzone.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "voluptueuse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vrcholovka.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "walkera-fans.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "walltime.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "walpu.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "walpuski.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webdesignsandiego.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webgreat.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "website-engineering.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weiming.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wer.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "westcentralaor.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whnpa.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whosyourdaddy.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whta.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wilkushka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wilkushka.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wineonthewall.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wm-access.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wm-access.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wmaccess.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wolfermann.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wolkoopjes.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "worksitevr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpexplorer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpspeed.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wrdx.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wsl.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "www.govt.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xentox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xinex.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xinnixwebshop.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xiongx.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--6o8h.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--80ac0aqlt.xn--p1ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--hwt895j.xn--kpry57d", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xone.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xq55.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yannik-buerkle.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yeswecan.co.bw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yhndnzj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youjizz.bz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ysun.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yuppi.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yvonnethomet.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zebulon.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zeetoppers.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zephyrbk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zephyrbookkeeping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zerg.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhangsidan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zhoutiancai.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zings.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zobworks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zoolaboo.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zozzle.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zxavier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/proxy_resolution/pac_file_fetcher_impl_unittest.cc b/net/proxy_resolution/pac_file_fetcher_impl_unittest.cc
index 35f8df6..600a9321 100644
--- a/net/proxy_resolution/pac_file_fetcher_impl_unittest.cc
+++ b/net/proxy_resolution/pac_file_fetcher_impl_unittest.cc
@@ -88,7 +88,8 @@
         std::make_unique<TransportSecurityState>());
     storage_.set_cert_transparency_verifier(
         std::make_unique<MultiLogCTVerifier>());
-    storage_.set_ct_policy_enforcer(std::make_unique<CTPolicyEnforcer>());
+    storage_.set_ct_policy_enforcer(
+        std::make_unique<DefaultCTPolicyEnforcer>());
     storage_.set_proxy_resolution_service(ProxyResolutionService::CreateFixed(
         ProxyConfigWithAnnotation(no_proxy, TRAFFIC_ANNOTATION_FOR_TESTS)));
     storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
diff --git a/net/proxy_resolution/proxy_resolution_service.cc b/net/proxy_resolution/proxy_resolution_service.cc
index b09e8d1..1f01c283 100644
--- a/net/proxy_resolution/proxy_resolution_service.cc
+++ b/net/proxy_resolution/proxy_resolution_service.cc
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -385,6 +386,34 @@
   return url.ReplaceComponents(replacements);
 }
 
+// Do not change the enumerated value as it is relied on by histograms.
+enum class PacUrlSchemeForHistogram {
+  kOther = 0,
+
+  kHttp = 1,
+  kHttps = 2,
+  kFtp = 3,
+  kFile = 4,
+  kData = 5,
+
+  kMaxValue = kData,
+};
+
+PacUrlSchemeForHistogram GetPacUrlScheme(const GURL& pac_url) {
+  if (pac_url.SchemeIs("http"))
+    return PacUrlSchemeForHistogram::kHttp;
+  if (pac_url.SchemeIs("https"))
+    return PacUrlSchemeForHistogram::kHttps;
+  if (pac_url.SchemeIs("data"))
+    return PacUrlSchemeForHistogram::kData;
+  if (pac_url.SchemeIs("ftp"))
+    return PacUrlSchemeForHistogram::kFtp;
+  if (pac_url.SchemeIs("file"))
+    return PacUrlSchemeForHistogram::kFile;
+
+  return PacUrlSchemeForHistogram::kOther;
+}
+
 }  // namespace
 
 // ProxyResolutionService::InitProxyResolver ----------------------------------
@@ -1576,6 +1605,11 @@
                                         &fetched_config_, &effective_config));
   }
 
+  if (config.value().has_pac_url()) {
+    UMA_HISTOGRAM_ENUMERATION("Net.ProxyResolutionService.PacUrlScheme",
+                              GetPacUrlScheme(config.value().pac_url()));
+  }
+
   // Set the new configuration as the most recently fetched one.
   fetched_config_ = effective_config;
 
diff --git a/net/proxy_resolution/proxy_resolution_service_unittest.cc b/net/proxy_resolution/proxy_resolution_service_unittest.cc
index 92addc9..6f42ddb 100644
--- a/net/proxy_resolution/proxy_resolution_service_unittest.cc
+++ b/net/proxy_resolution/proxy_resolution_service_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
@@ -171,6 +172,12 @@
       observer.OnProxyConfigChanged(config_, availability_);
   }
 
+  void SetPacUrlConfig(base::StringPiece pac_url) {
+    SetConfig(ProxyConfigWithAnnotation(
+        ProxyConfig::CreateFromCustomPacURL(GURL(pac_url)),
+        TRAFFIC_ANNOTATION_FOR_TESTS));
+  }
+
  private:
   ConfigAvailability availability_;
   ProxyConfigWithAnnotation config_;
@@ -333,6 +340,42 @@
   return GetJobsForURLs(map, urls);
 }
 
+// Helper class to verify the bucket counts for PacUrlScheme histogram.
+class PacUrlSchemeHistogramTester {
+ public:
+  void VerifyHistogram() const {
+    const char kPacUrlSchemeHistogram[] =
+        "Net.ProxyResolutionService.PacUrlScheme";
+
+    int total = GetTotal();
+
+    histograms_.ExpectTotalCount(kPacUrlSchemeHistogram, total);
+
+    if (total > 0) {
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 0, num_other);
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 1, num_http);
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 2, num_https);
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 3, num_ftp);
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 4, num_file);
+      histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 5, num_data);
+    }
+  }
+
+  int num_http = 0;
+  int num_https = 0;
+  int num_ftp = 0;
+  int num_data = 0;
+  int num_file = 0;
+  int num_other = 0;
+
+ private:
+  int GetTotal() const {
+    return num_http + num_https + num_ftp + num_data + num_file + num_other;
+  }
+
+  base::HistogramTester histograms_;
+};
+
 }  // namespace
 
 TEST_F(ProxyResolutionServiceTest, Direct) {
@@ -3576,4 +3619,58 @@
   EXPECT_TRUE(info.is_direct());
 }
 
+// Tests that the URL scheme for PAC files gets output to the histogram.
+TEST_F(ProxyResolutionServiceTest, PacUrlSchemeHistogram) {
+  PacUrlSchemeHistogramTester pac_histogram;
+
+  MockProxyConfigService* config_service =
+      new MockProxyConfigService(ProxyConfig::CreateDirect());
+
+  ProxyResolutionService service(
+      base::WrapUnique(config_service),
+      std::make_unique<MockAsyncProxyResolverFactory>(false), nullptr);
+
+  pac_histogram.VerifyHistogram();
+
+  // Set an http:// PAC.
+  config_service->SetPacUrlConfig("http://example.test/");
+  pac_histogram.num_http++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an https:// PAC.
+  config_service->SetPacUrlConfig("hTTps://example.test/wpad.dat");
+  pac_histogram.num_https++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an ftp:// PAC.
+  config_service->SetPacUrlConfig("ftp://example.test/pac.js");
+  pac_histogram.num_ftp++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an file:// PAC.
+  config_service->SetPacUrlConfig("file://example.test/boo");
+  pac_histogram.num_file++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an mailto: PAC.
+  config_service->SetPacUrlConfig("mailto:foo@example.test");
+  pac_histogram.num_other++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an data: PAC.
+  config_service->SetPacUrlConfig("data:,Hello%2C%20World!");
+  pac_histogram.num_data++;
+  pac_histogram.VerifyHistogram();
+
+  // Set an filesystem: PAC.
+  config_service->SetPacUrlConfig("filesystem:http://example.test/pac.js");
+  pac_histogram.num_other++;
+  pac_histogram.VerifyHistogram();
+
+  // Set another https:// as PAC.
+  config_service->SetPacUrlConfig("https://example2.test/wpad.dat");
+  pac_histogram.num_https++;
+  pac_histogram.VerifyHistogram();
+}
+
 }  // namespace net
diff --git a/net/quic/chromium/crypto/proof_verifier_chromium.cc b/net/quic/chromium/crypto/proof_verifier_chromium.cc
index b63bbca..5cafa7f00 100644
--- a/net/quic/chromium/crypto/proof_verifier_chromium.cc
+++ b/net/quic/chromium/crypto/proof_verifier_chromium.cc
@@ -391,7 +391,7 @@
   if (enforce_policy_checking_ &&
       (result == OK ||
        (IsCertificateError(result) && IsCertStatusMinorError(cert_status)))) {
-    SCTList verified_scts = ct::SCTsMatchingStatus(
+    ct::SCTList verified_scts = ct::SCTsMatchingStatus(
         verify_details_->ct_verify_result.scts, ct::SCT_STATUS_OK);
 
     verify_details_->ct_verify_result.policy_compliance =
@@ -399,7 +399,9 @@
             cert_verify_result.verified_cert.get(), verified_scts, net_log_);
     if (verify_details_->cert_verify_result.cert_status & CERT_STATUS_IS_EV) {
       if (verify_details_->ct_verify_result.policy_compliance !=
-          ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS) {
+              ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS &&
+          verify_details_->ct_verify_result.policy_compliance !=
+              ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY) {
         verify_details_->cert_verify_result.cert_status |=
             CERT_STATUS_CT_COMPLIANCE_FAILED;
         verify_details_->cert_verify_result.cert_status &= ~CERT_STATUS_IS_EV;
diff --git a/net/quic/chromium/crypto_test_utils_chromium.cc b/net/quic/chromium/crypto_test_utils_chromium.cc
index 4ced45f..7e58c1ac 100644
--- a/net/quic/chromium/crypto_test_utils_chromium.cc
+++ b/net/quic/chromium/crypto_test_utils_chromium.cc
@@ -99,7 +99,7 @@
   return std::make_unique<TestProofVerifierChromium>(
       std::move(cert_verifier), std::make_unique<TransportSecurityState>(),
       std::make_unique<MultiLogCTVerifier>(),
-      std::make_unique<CTPolicyEnforcer>(), "quic-root.pem");
+      std::make_unique<DefaultCTPolicyEnforcer>(), "quic-root.pem");
 }
 
 ProofVerifyContext* ProofVerifyContextForTesting() {
diff --git a/net/quic/chromium/quic_end_to_end_unittest.cc b/net/quic/chromium/quic_end_to_end_unittest.cc
index c03f990..7344f7b 100644
--- a/net/quic/chromium/quic_end_to_end_unittest.cc
+++ b/net/quic/chromium/quic_end_to_end_unittest.cc
@@ -239,7 +239,7 @@
   std::unique_ptr<ChannelIDService> channel_id_service_;
   TransportSecurityState transport_security_state_;
   std::unique_ptr<CTVerifier> cert_transparency_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
   std::unique_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index 011ab3c..3bfaca5 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -902,7 +902,7 @@
   MockCertVerifier cert_verifier_;
   TransportSecurityState transport_security_state_;
   std::unique_ptr<CTVerifier> cert_transparency_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   TestSocketPerformanceWatcherFactory test_socket_performance_watcher_factory_;
   scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
@@ -6122,7 +6122,7 @@
   MockCertVerifier cert_verifier_;
   TransportSecurityState transport_security_state_;
   std::unique_ptr<CTVerifier> cert_transparency_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   TestSocketPerformanceWatcherFactory test_socket_performance_watcher_factory_;
   scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
diff --git a/net/quic/chromium/quic_stream_factory_fuzzer.cc b/net/quic/chromium/quic_stream_factory_fuzzer.cc
index decfe07..96fa6c7 100644
--- a/net/quic/chromium/quic_stream_factory_fuzzer.cc
+++ b/net/quic/chromium/quic_stream_factory_fuzzer.cc
@@ -8,6 +8,7 @@
 
 #include "net/base/completion_once_callback.h"
 #include "net/base/test_completion_callback.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/do_nothing_ct_verifier.h"
 #include "net/cert/mock_cert_verifier.h"
 #include "net/cert/x509_certificate.h"
@@ -79,7 +80,7 @@
   QuicTagVector connection_options;
   QuicTagVector client_connection_options;
   std::unique_ptr<CTVerifier> cert_transparency_verifier;
-  CTPolicyEnforcer ct_policy_enforcer;
+  DefaultCTPolicyEnforcer ct_policy_enforcer;
 };
 
 static struct Env* env = new Env();
@@ -177,12 +178,14 @@
     return 0;
 
   // TODO(nedwilliamson): attempt connection migration here
-  stream->ReadResponseHeaders(callback.callback());
+  int rv = stream->ReadResponseHeaders(callback.callback());
+  if (rv != OK && rv != ERR_IO_PENDING) {
+    return 0;
+  }
   callback.WaitForResult();
 
   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
-  int rv =
-      stream->ReadResponseBody(buffer.get(), kBufferSize, callback.callback());
+  rv = stream->ReadResponseBody(buffer.get(), kBufferSize, callback.callback());
   if (rv == ERR_IO_PENDING)
     callback.WaitForResult();
 
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 361dfbc..b6b36390 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -801,7 +801,7 @@
   std::unique_ptr<ChannelIDService> channel_id_service_;
   TransportSecurityState transport_security_state_;
   std::unique_ptr<CTVerifier> cert_transparency_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   std::unique_ptr<ScopedMockNetworkChangeNotifier>
       scoped_mock_network_change_notifier_;
   std::unique_ptr<QuicStreamFactory> factory_;
diff --git a/net/quic/platform/impl/quic_ip_address_impl.cc b/net/quic/platform/impl/quic_ip_address_impl.cc
index 961f5f1..1fd9a15 100644
--- a/net/quic/platform/impl/quic_ip_address_impl.cc
+++ b/net/quic/platform/impl/quic_ip_address_impl.cc
@@ -4,13 +4,14 @@
 
 #include "net/quic/platform/impl/quic_ip_address_impl.h"
 
+#include "build/build_config.h"
 #include "net/base/address_family.h"
 #include "net/quic/platform/api/quic_bug_tracker.h"
 
 #if defined(OS_WIN)
 #include <winsock2.h>
 #include <ws2bth.h>
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <netinet/in.h>
 #endif
 
diff --git a/net/socket/socket.cc b/net/socket/socket.cc
index 07e61d1..39e3db0 100644
--- a/net/socket/socket.cc
+++ b/net/socket/socket.cc
@@ -17,4 +17,8 @@
   return ERR_READ_IF_READY_NOT_IMPLEMENTED;
 }
 
+int Socket::CancelReadIfReady() {
+  return ERR_READ_IF_READY_NOT_IMPLEMENTED;
+}
+
 }  // namespace net
diff --git a/net/socket/socket.h b/net/socket/socket.h
index 8b6eb53..e834108 100644
--- a/net/socket/socket.h
+++ b/net/socket/socket.h
@@ -52,6 +52,11 @@
                           int buf_len,
                           CompletionOnceCallback callback);
 
+  // Cancels a pending ReadIfReady(). May only be called when a ReadIfReady() is
+  // pending. Returns net::OK or an error code. ERR_READ_IF_READY_NOT_SUPPORTED
+  // is returned if ReadIfReady() is not supported.
+  virtual int CancelReadIfReady();
+
   // Writes data, up to |buf_len| bytes, to the socket.  Note: data may be
   // written partially.  The number of bytes written is returned, or an error
   // is returned upon failure.  ERR_SOCKET_NOT_CONNECTED should be returned if
diff --git a/net/socket/socket_descriptor.cc b/net/socket/socket_descriptor.cc
index 5e9623c..5163d76 100644
--- a/net/socket/socket_descriptor.cc
+++ b/net/socket/socket_descriptor.cc
@@ -4,14 +4,12 @@
 
 #include "net/socket/socket_descriptor.h"
 
-#if defined(OS_POSIX)
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-
 #if defined(OS_WIN)
 #include <ws2tcpip.h>
 #include "net/base/winsock_init.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/socket.h>
+#include <sys/types.h>
 #endif
 
 #if defined(OS_MACOSX)
@@ -34,7 +32,7 @@
     }
   }
   return result;
-#else  // OS_WIN
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   SocketDescriptor result = ::socket(family, type, protocol);
 #if defined(OS_MACOSX)
   // Disable SIGPIPE on this socket. Although Chromium globally disables
diff --git a/net/socket/socket_descriptor.h b/net/socket/socket_descriptor.h
index e58e140..9a42a96 100644
--- a/net/socket/socket_descriptor.h
+++ b/net/socket/socket_descriptor.h
@@ -14,12 +14,12 @@
 
 namespace net {
 
-#if defined(OS_POSIX)
-typedef int SocketDescriptor;
-const SocketDescriptor kInvalidSocket = -1;
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
 typedef SOCKET SocketDescriptor;
 const SocketDescriptor kInvalidSocket = INVALID_SOCKET;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+typedef int SocketDescriptor;
+const SocketDescriptor kInvalidSocket = -1;
 #endif
 
 // Creates  socket. See WSASocket/socket documentation of parameters.
diff --git a/net/socket/socket_options.cc b/net/socket/socket_options.cc
index 9b26aa2d..9589110 100644
--- a/net/socket/socket_options.cc
+++ b/net/socket/socket_options.cc
@@ -9,21 +9,21 @@
 #include "build/build_config.h"
 #include "net/base/net_errors.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_WIN)
+#include <winsock2.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <sys/socket.h>
-#elif defined(OS_WIN)
-#include <winsock2.h>
 #endif
 
 namespace net {
 
 int SetTCPNoDelay(SocketDescriptor fd, bool no_delay) {
-#if defined(OS_POSIX)
-  int on = no_delay ? 1 : 0;
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   BOOL on = no_delay ? TRUE : FALSE;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  int on = no_delay ? 1 : 0;
 #endif
   int rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
                       reinterpret_cast<const char*>(&on), sizeof(on));
@@ -44,10 +44,10 @@
 // provided on Linux prior to 3.9.
 //
 // SO_REUSEPORT is provided in MacOS X and iOS.
-#if defined(OS_POSIX)
-  int boolean_value = reuse ? 1 : 0;
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   BOOL boolean_value = reuse ? TRUE : FALSE;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  int boolean_value = reuse ? 1 : 0;
 #endif
   int rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
                       reinterpret_cast<const char*>(&boolean_value),
@@ -58,10 +58,10 @@
 int SetSocketReceiveBufferSize(SocketDescriptor fd, int32_t size) {
   int rv = setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
                       reinterpret_cast<const char*>(&size), sizeof(size));
-#if defined(OS_POSIX)
-  int os_error = errno;
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   int os_error = WSAGetLastError();
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  int os_error = errno;
 #endif
   int net_error = (rv == -1) ? MapSystemError(os_error) : OK;
   DCHECK(!rv) << "Could not set socket receive buffer size: " << net_error;
@@ -71,10 +71,10 @@
 int SetSocketSendBufferSize(SocketDescriptor fd, int32_t size) {
   int rv = setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
                       reinterpret_cast<const char*>(&size), sizeof(size));
-#if defined(OS_POSIX)
-  int os_error = errno;
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
   int os_error = WSAGetLastError();
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  int os_error = errno;
 #endif
   int net_error = (rv == -1) ? MapSystemError(os_error) : OK;
   DCHECK(!rv) << "Could not set socket receive buffer size: " << net_error;
diff --git a/net/socket/socket_posix.cc b/net/socket/socket_posix.cc
index 10e2e45..cabd3b8 100644
--- a/net/socket/socket_posix.cc
+++ b/net/socket/socket_posix.cc
@@ -348,6 +348,16 @@
   return ERR_IO_PENDING;
 }
 
+int SocketPosix::CancelReadIfReady() {
+  DCHECK(read_if_ready_callback_);
+
+  bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
+  DCHECK(ok);
+
+  read_if_ready_callback_.Reset();
+  return net::OK;
+}
+
 int SocketPosix::Write(
     IOBuffer* buf,
     int buf_len,
diff --git a/net/socket/socket_posix.h b/net/socket/socket_posix.h
index 9e0dd95..ff7cbb6 100644
--- a/net/socket/socket_posix.h
+++ b/net/socket/socket_posix.h
@@ -74,6 +74,7 @@
   // reading. This method doesn't hold on to |buf|.
   // See socket.h for more information.
   int ReadIfReady(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
+  int CancelReadIfReady();
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 93ad63f..dfedd36 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -504,6 +504,12 @@
   return helper_.AllReadDataConsumed();
 }
 
+void SequencedSocketData::CancelPendingRead() {
+  DCHECK_EQ(PENDING, read_state_);
+
+  read_state_ = IDLE;
+}
+
 bool SequencedSocketData::AllWriteDataConsumed() const {
   return helper_.AllWriteDataConsumed();
 }
@@ -929,6 +935,14 @@
   return ReadIfReadyImpl(buf, buf_len, std::move(callback));
 }
 
+int MockTCPClientSocket::CancelReadIfReady() {
+  DCHECK(pending_read_if_ready_callback_);
+
+  pending_read_if_ready_callback_.Reset();
+  data_->CancelPendingRead();
+  return OK;
+};
+
 int MockTCPClientSocket::Write(
     IOBuffer* buf,
     int buf_len,
@@ -945,6 +959,11 @@
 
   was_used_to_convey_data_ = true;
 
+  if (write_result.result == ERR_CONNECTION_CLOSED) {
+    // This MockWrite is just a marker to instruct us to set
+    // peer_closed_connection_.
+    peer_closed_connection_ = true;
+  }
   // ERR_IO_PENDING is a signal that the socket data will call back
   // asynchronously later.
   if (write_result.result == ERR_IO_PENDING) {
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 1ffc54e4..355d5dc4 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -220,6 +220,7 @@
   virtual MockWriteResult OnWrite(const std::string& data) = 0;
   virtual bool AllReadDataConsumed() const = 0;
   virtual bool AllWriteDataConsumed() const = 0;
+  virtual void CancelPendingRead() {}
 
   virtual void OnEnableTCPFastOpenIfSupported();
 
@@ -416,6 +417,7 @@
   bool AllWriteDataConsumed() const override;
   void OnEnableTCPFastOpenIfSupported() override;
   bool IsIdle() const override;
+  void CancelPendingRead() override;
 
   // An ASYNC read event with a return value of ERR_IO_PENDING will cause the
   // socket data to pause at that event, and advance no further, until Resume is
@@ -658,6 +660,7 @@
   int ReadIfReady(IOBuffer* buf,
                   int buf_len,
                   CompletionOnceCallback callback) override;
+  int CancelReadIfReady() override;
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 41409fc..29634c5 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1501,14 +1501,16 @@
       host_and_port().host(), server_cert_verify_result_.verified_cert.get(),
       ocsp_response, sct_list, &ct_verify_result_.scts, net_log_);
 
-  SCTList verified_scts =
+  ct::SCTList verified_scts =
       ct::SCTsMatchingStatus(ct_verify_result_.scts, ct::SCT_STATUS_OK);
 
   ct_verify_result_.policy_compliance = policy_enforcer_->CheckCompliance(
       server_cert_verify_result_.verified_cert.get(), verified_scts, net_log_);
   if (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV) {
     if (ct_verify_result_.policy_compliance !=
-        ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS) {
+            ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS &&
+        ct_verify_result_.policy_compliance !=
+            ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY) {
       server_cert_verify_result_.cert_status |=
           CERT_STATUS_CT_COMPLIANCE_FAILED;
       server_cert_verify_result_.cert_status &= ~CERT_STATUS_IS_EV;
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 2013737..7ec62aa 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -199,7 +199,7 @@
   std::unique_ptr<MockCertVerifier> cert_verifier_;
   std::unique_ptr<TransportSecurityState> transport_security_state_;
   MultiLogCTVerifier ct_verifier_;
-  CTPolicyEnforcer ct_policy_enforcer_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   const std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
   const scoped_refptr<SSLConfigService> ssl_config_service_;
   const std::unique_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
index 31cef1d..d45a1051 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -93,7 +93,7 @@
   ~MockCTPolicyEnforcer() override = default;
   ct::CTPolicyCompliance CheckCompliance(
       X509Certificate* cert,
-      const SCTList& verified_scts,
+      const ct::SCTList& verified_scts,
       const NetLogWithSource& net_log) override {
     return ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
   }
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index 93c1c00..61cffe20 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -298,6 +298,10 @@
   return ReadCommon(buf, buf_len, std::move(callback), /*read_if_ready=*/true);
 }
 
+int TCPClientSocket::CancelReadIfReady() {
+  return socket_->CancelReadIfReady();
+}
+
 int TCPClientSocket::Write(
     IOBuffer* buf,
     int buf_len,
diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h
index b7a9113..c2a229fc5 100644
--- a/net/socket/tcp_client_socket.h
+++ b/net/socket/tcp_client_socket.h
@@ -77,6 +77,7 @@
   int ReadIfReady(IOBuffer* buf,
                   int buf_len,
                   CompletionOnceCallback callback) override;
+  int CancelReadIfReady() override;
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
diff --git a/net/socket/tcp_socket.h b/net/socket/tcp_socket.h
index 34f1eba..04a4147 100644
--- a/net/socket/tcp_socket.h
+++ b/net/socket/tcp_socket.h
@@ -11,7 +11,7 @@
 
 #if defined(OS_WIN)
 #include "net/socket/tcp_socket_win.h"
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include "net/socket/tcp_socket_posix.h"
 #endif
 
@@ -25,7 +25,7 @@
 // before you know whether it is a client or server socket).
 #if defined(OS_WIN)
 typedef TCPSocketWin TCPSocket;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 typedef TCPSocketPosix TCPSocket;
 #endif
 
diff --git a/net/socket/tcp_socket_posix.cc b/net/socket/tcp_socket_posix.cc
index fd9838e..12df91a5 100644
--- a/net/socket/tcp_socket_posix.cc
+++ b/net/socket/tcp_socket_posix.cc
@@ -396,6 +396,12 @@
   return rv;
 }
 
+int TCPSocketPosix::CancelReadIfReady() {
+  DCHECK(socket_);
+
+  return socket_->CancelReadIfReady();
+}
+
 int TCPSocketPosix::Write(
     IOBuffer* buf,
     int buf_len,
diff --git a/net/socket/tcp_socket_posix.h b/net/socket/tcp_socket_posix.h
index ac51718b..4512945f 100644
--- a/net/socket/tcp_socket_posix.h
+++ b/net/socket/tcp_socket_posix.h
@@ -89,6 +89,7 @@
   // Returns a net error code.
   int Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
   int ReadIfReady(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
+  int CancelReadIfReady();
 
   // Writes to the socket.
   // Returns a net error code.
diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc
index fa48dfb..5b97c310 100644
--- a/net/socket/tcp_socket_unittest.cc
+++ b/net/socket/tcp_socket_unittest.cc
@@ -528,6 +528,62 @@
   run_loop.Run();
 }
 
+// If a ReadIfReady is pending, it's legal to cancel it and start reading later.
+TEST_F(TCPSocketTest, CancelPendingReadIfReady) {
+  ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4());
+
+  // Create a connected socket.
+  TestCompletionCallback connect_callback;
+  std::unique_ptr<TCPSocket> connecting_socket =
+      std::make_unique<TCPSocket>(nullptr, nullptr, NetLogSource());
+  int result = connecting_socket->Open(ADDRESS_FAMILY_IPV4);
+  ASSERT_THAT(result, IsOk());
+  int connect_result =
+      connecting_socket->Connect(local_address_, connect_callback.callback());
+
+  TestCompletionCallback accept_callback;
+  std::unique_ptr<TCPSocket> accepted_socket;
+  IPEndPoint accepted_address;
+  result = socket_.Accept(&accepted_socket, &accepted_address,
+                          accept_callback.callback());
+  ASSERT_THAT(accept_callback.GetResult(result), IsOk());
+  ASSERT_TRUE(accepted_socket.get());
+  ASSERT_THAT(connect_callback.GetResult(connect_result), IsOk());
+
+  // Try to read from the socket, but never write anything to the other end.
+  base::RunLoop run_loop;
+  scoped_refptr<IOBufferWithDestructionCallback> read_buffer(
+      base::MakeRefCounted<IOBufferWithDestructionCallback>(
+          run_loop.QuitClosure()));
+  TestCompletionCallback read_callback;
+  EXPECT_EQ(ERR_IO_PENDING, connecting_socket->ReadIfReady(
+                                read_buffer.get(), read_buffer->size(),
+                                read_callback.callback()));
+
+  // Now cancel the pending ReadIfReady().
+  connecting_socket->CancelReadIfReady();
+
+  // Send data to |connecting_socket|.
+  const char kMsg[] = "hello!";
+  scoped_refptr<StringIOBuffer> write_buffer =
+      base::MakeRefCounted<StringIOBuffer>(kMsg);
+
+  TestCompletionCallback write_callback;
+  int write_result = accepted_socket->Write(write_buffer.get(), strlen(kMsg),
+                                            write_callback.callback(),
+                                            TRAFFIC_ANNOTATION_FOR_TESTS);
+  const int msg_size = strlen(kMsg);
+  ASSERT_EQ(msg_size, write_result);
+
+  // Try reading again. ReadIfReady() should still succeed.
+  TestCompletionCallback read_callback2;
+  int read_result = connecting_socket->ReadIfReady(
+      read_buffer.get(), read_buffer->size(), read_callback2.callback());
+
+  ASSERT_EQ(msg_size, read_callback2.GetResult(read_result));
+  ASSERT_EQ(0, memcmp(&kMsg, read_buffer->data(), msg_size));
+}
+
 // These tests require kernel support for tcp_info struct, and so they are
 // enabled only on certain platforms.
 #if defined(TCP_INFO) || defined(OS_LINUX)
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index 747430d4..2edf932 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -111,6 +111,9 @@
   void WatchForRead();
   void WatchForWrite();
 
+  // Stops watching for read.
+  void StopWatchingForRead();
+
   // The TCPSocketWin is going away.
   void Detach();
 
@@ -212,6 +215,12 @@
   write_watcher_.StartWatchingOnce(write_overlapped_.hEvent, &writer_);
 }
 
+void TCPSocketWin::Core::StopWatchingForRead() {
+  DCHECK(!socket_->waiting_connect_);
+
+  read_watcher_.StopWatching();
+}
+
 void TCPSocketWin::Core::Detach() {
   // Stop watching the read watcher. A read won't be signalled after the Detach
   // call, since the socket has been closed, but it's possible the event was
@@ -528,6 +537,17 @@
   return ERR_IO_PENDING;
 }
 
+int TCPSocketWin::CancelReadIfReady() {
+  DCHECK(read_callback_.is_null());
+  DCHECK(!read_if_ready_callback_.is_null());
+  DCHECK(waiting_read_);
+
+  core_->StopWatchingForRead();
+  read_if_ready_callback_.Reset();
+  waiting_read_ = false;
+  return net::OK;
+}
+
 int TCPSocketWin::Write(
     IOBuffer* buf,
     int buf_len,
diff --git a/net/socket/tcp_socket_win.h b/net/socket/tcp_socket_win.h
index 179b3c4..6b4594c 100644
--- a/net/socket/tcp_socket_win.h
+++ b/net/socket/tcp_socket_win.h
@@ -69,6 +69,7 @@
   // Full duplex mode (reading and writing at the same time) is supported.
   int Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
   int ReadIfReady(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
+  int CancelReadIfReady();
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
diff --git a/net/socket/udp_socket.h b/net/socket/udp_socket.h
index 1a0d4398..9f5f74c 100644
--- a/net/socket/udp_socket.h
+++ b/net/socket/udp_socket.h
@@ -9,7 +9,7 @@
 
 #if defined(OS_WIN)
 #include "net/socket/udp_socket_win.h"
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include "net/socket/udp_socket_posix.h"
 #endif
 
@@ -37,7 +37,7 @@
 //                             // address.
 #if defined(OS_WIN)
 typedef UDPSocketWin UDPSocket;
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 typedef UDPSocketPosix UDPSocket;
 #endif
 
diff --git a/net/spdy/chromium/spdy_test_util_common.cc b/net/spdy/chromium/spdy_test_util_common.cc
index 5ec9387..6145d25 100644
--- a/net/spdy/chromium/spdy_test_util_common.cc
+++ b/net/spdy/chromium/spdy_test_util_common.cc
@@ -311,7 +311,7 @@
       channel_id_service(nullptr),
       transport_security_state(std::make_unique<TransportSecurityState>()),
       cert_transparency_verifier(std::make_unique<DoNothingCTVerifier>()),
-      ct_policy_enforcer(std::make_unique<CTPolicyEnforcer>()),
+      ct_policy_enforcer(std::make_unique<DefaultCTPolicyEnforcer>()),
       proxy_resolution_service(std::move(proxy_resolution_service)),
       ssl_config_service(base::MakeRefCounted<SSLConfigServiceDefaults>()),
       socket_factory(std::make_unique<MockClientSocketFactory>()),
@@ -413,27 +413,13 @@
   return context;
 }
 
-class AllowAnyCertCTPolicyEnforcer : public CTPolicyEnforcer {
- public:
-  AllowAnyCertCTPolicyEnforcer() = default;
-  ~AllowAnyCertCTPolicyEnforcer() override = default;
-
-  ct::CTPolicyCompliance CheckCompliance(
-      X509Certificate* cert,
-      const SCTList& verified_scts,
-      const NetLogWithSource& net_log) override {
-    return ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-};
-
 SpdyURLRequestContext::SpdyURLRequestContext() : storage_(this) {
   storage_.set_host_resolver(std::make_unique<MockHostResolver>());
   storage_.set_cert_verifier(std::make_unique<MockCertVerifier>());
   storage_.set_transport_security_state(
       std::make_unique<TransportSecurityState>());
   storage_.set_proxy_resolution_service(ProxyResolutionService::CreateDirect());
-  storage_.set_ct_policy_enforcer(
-      std::make_unique<AllowAnyCertCTPolicyEnforcer>());
+  storage_.set_ct_policy_enforcer(std::make_unique<DefaultCTPolicyEnforcer>());
   storage_.set_cert_transparency_verifier(
       std::make_unique<DoNothingCTVerifier>());
   storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index 1ab610c..3e9e6850 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -49,6 +49,7 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_known_logs.h"
 #include "net/cert/ct_log_verifier.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/http/transport_security_state.h"
 #include "net/quic/chromium/crypto/proof_verifier_chromium.h"
@@ -67,7 +68,6 @@
 #include "net/tools/quic/synchronous_host_resolver.h"
 
 using net::CertVerifier;
-using net::CTPolicyEnforcer;
 using net::CTVerifier;
 using net::MultiLogCTVerifier;
 using net::ProofVerifier;
@@ -269,7 +269,8 @@
       new TransportSecurityState);
   std::unique_ptr<MultiLogCTVerifier> ct_verifier(new MultiLogCTVerifier());
   ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs());
-  std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer(new CTPolicyEnforcer());
+  std::unique_ptr<net::CTPolicyEnforcer> ct_policy_enforcer(
+      new net::DefaultCTPolicyEnforcer());
   std::unique_ptr<ProofVerifier> proof_verifier;
   if (line->HasSwitch("disable-certificate-verification")) {
     proof_verifier = net::QuicMakeUnique<FakeProofVerifier>();
diff --git a/net/tools/quic/quic_simple_client_bin.cc b/net/tools/quic/quic_simple_client_bin.cc
index 91fdf87..d807a0e2 100644
--- a/net/tools/quic/quic_simple_client_bin.cc
+++ b/net/tools/quic/quic_simple_client_bin.cc
@@ -50,6 +50,7 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_known_logs.h"
 #include "net/cert/ct_log_verifier.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/http/transport_security_state.h"
 #include "net/quic/chromium/crypto/proof_verifier_chromium.h"
@@ -67,7 +68,6 @@
 #include "url/gurl.h"
 
 using net::CertVerifier;
-using net::CTPolicyEnforcer;
 using net::CTVerifier;
 using net::MultiLogCTVerifier;
 using net::ProofVerifier;
@@ -267,7 +267,8 @@
       new TransportSecurityState);
   std::unique_ptr<MultiLogCTVerifier> ct_verifier(new MultiLogCTVerifier());
   ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs());
-  std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer(new CTPolicyEnforcer());
+  std::unique_ptr<net::CTPolicyEnforcer> ct_policy_enforcer(
+      new net::DefaultCTPolicyEnforcer());
   std::unique_ptr<ProofVerifier> proof_verifier;
   if (line->HasSwitch("disable-certificate-verification")) {
     proof_verifier.reset(new FakeProofVerifier());
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 26ee4ca..cb4a829 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -496,7 +496,8 @@
   if (ct_policy_enforcer_) {
     storage->set_ct_policy_enforcer(std::move(ct_policy_enforcer_));
   } else {
-    storage->set_ct_policy_enforcer(std::make_unique<CTPolicyEnforcer>());
+    storage->set_ct_policy_enforcer(
+        std::make_unique<DefaultCTPolicyEnforcer>());
   }
 
   if (throttling_enabled_) {
diff --git a/net/url_request/url_request_file_dir_job.cc b/net/url_request/url_request_file_dir_job.cc
index 1d44ad6..5610f8e 100644
--- a/net/url_request/url_request_file_dir_job.cc
+++ b/net/url_request/url_request_file_dir_job.cc
@@ -14,12 +14,13 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "net/base/directory_listing.h"
 #include "net/base/io_buffer.h"
 #include "net/url_request/url_request_status.h"
 #include "url/gurl.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <sys/stat.h>
 #endif
 
@@ -102,7 +103,7 @@
 
 #if defined(OS_WIN)
     const base::string16& title = dir_path_.value();
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     // TODO(jungshik): Add SysNativeMBToUTF16 to sys_string_conversions.
     // On Mac, need to add NFKC->NFC conversion either here or in file_path.
     // On Linux, the file system encoding is not defined, but we assume that
@@ -131,7 +132,7 @@
       filename.value() != base::FilePath::kParentDirectory) {
 #if defined(OS_WIN)
     std::string raw_bytes;  // Empty on Windows means UTF-8 encoded name.
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     // TODO(jungshik): The same issue as for the directory name.
     const std::string& raw_bytes = filename.value();
 #endif
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 8106e65f..46b5d68 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -86,7 +86,7 @@
   }
   if (!ct_policy_enforcer()) {
     context_storage_.set_ct_policy_enforcer(
-        std::make_unique<CTPolicyEnforcer>());
+        std::make_unique<DefaultCTPolicyEnforcer>());
   }
   if (!ssl_config_service())
     context_storage_.set_ssl_config_service(new SSLConfigServiceDefaults());
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 04301d5..d14529b 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -27,6 +27,7 @@
 #include "net/base/network_delegate_impl.h"
 #include "net/base/request_priority.h"
 #include "net/cert/cert_verifier.h"
+#include "net/cert/ct_policy_enforcer.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/ftp/ftp_network_layer.h"
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 464ae90..b3a71959 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -7216,7 +7216,7 @@
 
   ct::CTPolicyCompliance CheckCompliance(
       X509Certificate* cert,
-      const SCTList& verified_scts,
+      const ct::SCTList& verified_scts,
       const NetLogWithSource& net_log) override {
     return default_result_;
   }
@@ -10779,8 +10779,7 @@
   }
 
   void SetUp() override {
-    context_.SetCTPolicyEnforcer(
-        std::make_unique<AllowAnyCertCTPolicyEnforcer>());
+    context_.SetCTPolicyEnforcer(std::make_unique<DefaultCTPolicyEnforcer>());
     SetupContext();
     context_.Init();
 
@@ -10847,18 +10846,6 @@
   }
 
  protected:
-  class AllowAnyCertCTPolicyEnforcer : public CTPolicyEnforcer {
-   public:
-    AllowAnyCertCTPolicyEnforcer() = default;
-    ~AllowAnyCertCTPolicyEnforcer() override = default;
-
-    ct::CTPolicyCompliance CheckCompliance(
-        X509Certificate* cert,
-        const SCTList& verified_scts,
-        const NetLogWithSource& net_log) override {
-      return ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-    }
-  };
   // SetupContext configures the URLRequestContext that will be used for making
   // connetions to testserver. This can be overridden in test subclasses for
   // different behaviour.
diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.cc b/remoting/protocol/ssl_hmac_channel_authenticator.cc
index b03d4cc..709534745 100644
--- a/remoting/protocol/ssl_hmac_channel_authenticator.cc
+++ b/remoting/protocol/ssl_hmac_channel_authenticator.cc
@@ -95,20 +95,6 @@
   }
 };
 
-// A CTPolicyEnforcer that accepts all certificates.
-class IgnoresCTPolicyEnforcer : public net::CTPolicyEnforcer {
- public:
-  IgnoresCTPolicyEnforcer() = default;
-  ~IgnoresCTPolicyEnforcer() override = default;
-
-  net::ct::CTPolicyCompliance CheckCompliance(
-      net::X509Certificate* cert,
-      const net::SCTList& verified_scts,
-      const net::NetLogWithSource& net_log) override {
-    return net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
-  }
-};
-
 // Implements net::StreamSocket interface on top of P2PStreamSocket to be passed
 // to net::SSLClientSocket and net::SSLServerSocket.
 class NetStreamSocketAdapter : public net::StreamSocket {
@@ -300,7 +286,7 @@
     transport_security_state_.reset(new net::TransportSecurityState);
     cert_verifier_.reset(new FailingCertVerifier);
     ct_verifier_.reset(new net::DoNothingCTVerifier);
-    ct_policy_enforcer_.reset(new IgnoresCTPolicyEnforcer);
+    ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer);
 
     net::SSLConfig ssl_config;
     // Certificate verification and revocation checking are not needed
diff --git a/remoting/signaling/xmpp_signal_strategy.cc b/remoting/signaling/xmpp_signal_strategy.cc
index 1f09bf4..734a679 100644
--- a/remoting/signaling/xmpp_signal_strategy.cc
+++ b/remoting/signaling/xmpp_signal_strategy.cc
@@ -344,7 +344,7 @@
   cert_verifier_ = net::CertVerifier::CreateDefault();
   transport_security_state_.reset(new net::TransportSecurityState());
   cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
-  ct_policy_enforcer_.reset(new net::CTPolicyEnforcer());
+  ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer());
   net::SSLClientSocketContext context;
   context.cert_verifier = cert_verifier_.get();
   context.transport_security_state = transport_security_state_.get();
diff --git a/sandbox/win/src/process_mitigations_win32k_unittest.cc b/sandbox/win/src/process_mitigations_win32k_unittest.cc
index 48adf00..4e60300 100644
--- a/sandbox/win/src/process_mitigations_win32k_unittest.cc
+++ b/sandbox/win/src/process_mitigations_win32k_unittest.cc
@@ -629,12 +629,21 @@
   EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(test_policy_command.c_str()));
 }
 
+// Flaky in Debug. https://crbug.com/840335
+#if !defined(NDEBUG)
+#define MAYBE_CheckWin8LockDownSuccess DISABLED_CheckWin8LockDownSuccess
+#define MAYBE_CheckWin8Redirection DISABLED_CheckWin8Redirection
+#else
+#define MAYBE_CheckWin8LockDownSuccess CheckWin8LockDownSuccess
+#define MAYBE_CheckWin8Redirection CheckWin8Redirection
+#endif
+
 // This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation
 // along with the policy to fake user32 and gdi32 initialization successfully
 // launches the target process.
 // The test process itself links against user32/gdi32.
-// Flaky. https://crbug.com/840335
-TEST(ProcessMitigationsWin32kTest, DISABLED_CheckWin8LockDownSuccess) {
+
+TEST(ProcessMitigationsWin32kTest, MAYBE_CheckWin8LockDownSuccess) {
   if (base::win::GetVersion() < base::win::VERSION_WIN8)
     return;
 
@@ -661,7 +670,7 @@
 // This test validates the even though we're running under win32k lockdown
 // we can use the IPC redirection to enumerate the list of monitors.
 // Flaky. https://crbug.com/840335
-TEST(ProcessMitigationsWin32kTest, DISABLED_CheckWin8Redirection) {
+TEST(ProcessMitigationsWin32kTest, MAYBE_CheckWin8Redirection) {
   if (base::win::GetVersion() < base::win::VERSION_WIN8)
     return;
 
diff --git a/services/metrics/public/cpp/ukm_entry_builder_base.cc b/services/metrics/public/cpp/ukm_entry_builder_base.cc
index 9dc82637..6685a58 100644
--- a/services/metrics/public/cpp/ukm_entry_builder_base.cc
+++ b/services/metrics/public/cpp/ukm_entry_builder_base.cc
@@ -23,7 +23,7 @@
 
 void UkmEntryBuilderBase::SetMetricInternal(uint64_t metric_hash,
                                             int64_t value) {
-  entry_->metrics.emplace_back(mojom::UkmMetric::New(metric_hash, value));
+  entry_->metrics.emplace(metric_hash, value);
 }
 
 void UkmEntryBuilderBase::Record(UkmRecorder* recorder) {
diff --git a/services/metrics/public/mojom/ukm_interface.mojom b/services/metrics/public/mojom/ukm_interface.mojom
index 7a1b4fc..1d81731 100644
--- a/services/metrics/public/mojom/ukm_interface.mojom
+++ b/services/metrics/public/mojom/ukm_interface.mojom
@@ -6,15 +6,10 @@
 
 import "url/mojom/url.mojom";
 
-struct UkmMetric {
-  uint64 metric_hash;
-  int64 value;
-};
-
 struct UkmEntry {
   int64 source_id;
   uint64 event_hash;
-  array<UkmMetric> metrics;
+  map<uint64,int64> metrics;
 };
 
 interface UkmRecorderInterface {
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 2d7fe8ec..9e3885a 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -80,6 +80,8 @@
     "throttling/throttling_network_transaction_factory.h",
     "throttling/throttling_upload_data_stream.cc",
     "throttling/throttling_upload_data_stream.h",
+    "tls_client_socket.cc",
+    "tls_client_socket.h",
     "udp_socket.cc",
     "udp_socket.h",
     "upload_progress_tracker.cc",
@@ -172,6 +174,7 @@
     "test_chunked_data_pipe_getter.cc",
     "test_chunked_data_pipe_getter.h",
     "throttling/throttling_controller_unittest.cc",
+    "tls_client_socket_unittest.cc",
     "udp_socket_unittest.cc",
     "upload_progress_tracker_unittest.cc",
     "url_loader_unittest.cc",
@@ -179,6 +182,7 @@
 
   if (!is_ios) {
     sources += [
+      "network_context_cert_transparency_unittest.cc",
       "proxy_resolver_factory_mojo_unittest.cc",
       "websocket_throttler_unittest.cc",
     ]
@@ -188,6 +192,7 @@
     ":network_service",
     ":test_support",
     "//base",
+    "//components/certificate_transparency",
     "//components/network_session_configurator/browser",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
@@ -205,6 +210,8 @@
   testonly = true
 
   sources = [
+    "mojo_socket_test_util.cc",
+    "mojo_socket_test_util.h",
     "test/test_data_pipe_getter.cc",
     "test/test_data_pipe_getter.h",
     "test/test_network_context.h",
diff --git a/services/network/cross_origin_read_blocking.cc b/services/network/cross_origin_read_blocking.cc
index d63e63e6..eef5fc4 100644
--- a/services/network/cross_origin_read_blocking.cc
+++ b/services/network/cross_origin_read_blocking.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <algorithm>
 #include <string>
 #include <unordered_set>
 
@@ -92,79 +93,45 @@
 //   kMaybe.
 // - Returns kNo otherwise.
 //
-// Mutates |data| to advance past the comment when returning kYes.
-//
-// Additionally, if the body of the HTML comment (e.g. normally the kYes and
-// kMaybe cases) contains javascript-like comments (e.g. |data| == "<!--/*-->"),
-// then kNo will be returned (and |data| won't be mutated).  This helps avoid
-// CORB blocking of the html/js polyglots identified in
-// https://crbug.com/839425.
+// Mutates |data| to advance past the comment when returning kYes.  Note that
+// SingleLineHTMLCloseComment ECMAscript rule is taken into account which means
+// that characters following an HTML comment are consumed up to the nearest line
+// terminating character.
 SniffingResult MaybeSkipHtmlComment(StringPiece* data) {
-  static const StringPiece kBeginCommentSignature = "<!--";
-  if (!data->starts_with(kBeginCommentSignature)) {
-    if (kBeginCommentSignature.starts_with(*data))
+  constexpr StringPiece kStartString = "<!--";
+  if (!data->starts_with(kStartString)) {
+    if (kStartString.starts_with(*data))
       return CrossOriginReadBlocking::kMaybe;
     return CrossOriginReadBlocking::kNo;
   }
 
-  enum State {
-    kNothingSpecial,
-    kAfterDash,
-    kAfterDashDash,
-    kAfterSlash,
-  } state = kNothingSpecial;
-  for (size_t i = kBeginCommentSignature.length(); i < data->length(); i++) {
-    char c = (*data)[i];
-    switch (state) {
-      case kNothingSpecial:
-        if (c == '-')
-          state = kAfterDash;
-        else if (c == '/')
-          state = kAfterSlash;
-        else
-          DCHECK_EQ(kNothingSpecial, state);
-        break;
+  constexpr StringPiece kEndString = "-->";
+  size_t end_of_html_comment = data->find(kEndString, kStartString.length());
+  if (end_of_html_comment == StringPiece::npos)
+    return CrossOriginReadBlocking::kMaybe;
+  end_of_html_comment += kEndString.length();
 
-      case kAfterDash:
-        if (c == '-')
-          state = kAfterDashDash;
-        else if (c == '/')
-          state = kAfterSlash;
-        else
-          state = kNothingSpecial;
-        break;
+  // Skipping until the first line terminating character.  See
+  // https://crbug.com/839945 for the motivation behind this.
+  //
+  // https://www.ecma-international.org/ecma-262/8.0/index.html#sec-line-terminators
+  // defines <LF>, <CR>, <LS> ::= "\u2028", <PS> ::= "\u2029".
+  // https://www.ecma-international.org/ecma-262/8.0/index.html#prod-LineTerminator
+  // defines LineTerminator ::= <LF> | <CR> | <LS> | <PS>.
+  DCHECK_LT(data->length(), base::StringPiece::npos);
+  size_t end_of_line = base::StringPiece::npos;
+  end_of_line =
+      std::min(end_of_line, data->find_first_of("\r\n", end_of_html_comment));
+  end_of_line =
+      std::min(end_of_line, data->find("\u2028", end_of_html_comment));
+  end_of_line =
+      std::min(end_of_line, data->find("\u2029", end_of_html_comment));
+  if (end_of_line == base::StringPiece::npos)
+    return CrossOriginReadBlocking::kMaybe;
 
-      case kAfterDashDash:
-        if (c == '>') {
-          // Found the end of the HTML comment.
-          data->remove_prefix(i + 1);  // Advance past the comment.
-          return CrossOriginReadBlocking::kYes;
-        } else if (c == '-') {
-          state = kAfterDashDash;
-        } else if (c == '/') {
-          state = kAfterSlash;
-        } else {
-          state = kNothingSpecial;
-        }
-        break;
-
-      case kAfterSlash:
-        if (c == '/' || c == '*') {
-          // html/js polyglot - let's report that this is not HTML, to avoid
-          // blocking what may be a valid Javascript.
-          return CrossOriginReadBlocking::kNo;
-        }
-        if (c == '/')
-          state = kAfterSlash;
-        else if (c == '-')
-          state = kAfterDash;
-        else
-          state = kNothingSpecial;
-        break;
-    }
-  }
-
-  return CrossOriginReadBlocking::kMaybe;
+  // Found real end of the combined HTML/JS comment.
+  data->remove_prefix(end_of_line);
+  return CrossOriginReadBlocking::kYes;
 }
 
 // Headers from
diff --git a/services/network/cross_origin_read_blocking_explainer.md b/services/network/cross_origin_read_blocking_explainer.md
index 2ed45a0..dd10da7 100644
--- a/services/network/cross_origin_read_blocking_explainer.md
+++ b/services/network/cross_origin_read_blocking_explainer.md
@@ -262,17 +262,29 @@
   [normal HTML sniffing](https://mimesniff.spec.whatwg.org/#identifying-a-resource-with-an-unknown-mime-type))
   presence of "`<!--`" string doesn't immediately confirm that the sniffed resource is a
   HTML document - the HTML comment still has to be followed by a valid HTML tag.
-* Additionally if the HTML-style comment contains Javascript-style comments
-  (i.e. either "`/*`" or "`//`" substrings),
-  then CORB conservatively assumes that the resource is not a HTML document.
-  This helps avoid blocking html/javascript polyglots which have been observed
-  in use on real websites - see the example below:
+* Additionally, after the end of a HTML comment, the CORB sniffer will skip all
+  characters until a line terminating character.  This helps accomodate the
+  [`SingleLineHTMLCloseComment`](https://www.ecma-international.org/ecma-262/8.0/index.html#prod-annexB-SingleLineHTMLCloseComment)
+  rule which can consume
+  [`SingleLineCommentChars`](https://www.ecma-international.org/ecma-262/8.0/index.html#prod-SingleLineCommentChars)
+  _after_ the "`-->`" characters.
+
+Examples of html/javascript polyglots which have been observed
+in use on real websites:
 ```js
 <!--/*--><html><body><script type="text/javascript"><!--//*/
 var x = "This is both valid html and valid javascript";
 //--></script></body></html>
 ```
 
+```js
+<!-- comment --> <script type='text/javascript'>
+//<![CDATA[
+var x = "This is both valid html and valid javascript";
+//]]>--></script>
+```
+
+
 ### Protecting XML
 
 XML, like JSON, is a widely used data exchange format, and like HTML, is a
diff --git a/services/network/cross_origin_read_blocking_unittest.cc b/services/network/cross_origin_read_blocking_unittest.cc
index 68a1e39..e85db98 100644
--- a/services/network/cross_origin_read_blocking_unittest.cc
+++ b/services/network/cross_origin_read_blocking_unittest.cc
@@ -58,18 +58,18 @@
 
   // HTML comment followed by whitespace and valid HTML tags.
   EXPECT_EQ(SniffingResult::kYes,
-            CORB::SniffForHTML(" <!-- this is comment --> <html><body>"));
+            CORB::SniffForHTML(" <!-- this is comment -->\n<html><body>"));
 
   // HTML comment, whitespace, more HTML comments, HTML tags.
   EXPECT_EQ(
       SniffingResult::kYes,
       CORB::SniffForHTML(
-          "<!-- this is comment -->\n<!-- this is comment --><html><body>"));
+          "<!-- this is comment -->\n<!-- this is comment -->\n<html><body>"));
 
   // HTML comment followed by valid HTML tag.
   EXPECT_EQ(
       SniffingResult::kYes,
-      CORB::SniffForHTML("<!-- this is comment <!-- --><script></script>"));
+      CORB::SniffForHTML("<!-- this is comment <!-- -->\n<script></script>"));
 
   // Whitespace followed by valid Javascript.
   EXPECT_EQ(SniffingResult::kNo,
@@ -79,7 +79,7 @@
   EXPECT_EQ(
       SniffingResult::kNo,
       CORB::SniffForHTML(
-          " <!-- this is comment\n document.write(1);\n// -->window.open()"));
+          " <!-- this is comment\n document.write(1);\n// -->\nwindow.open()"));
 
   // HTML/Javascript polyglot should return kNo.
   EXPECT_EQ(SniffingResult::kNo,
@@ -88,15 +88,25 @@
                 "var blah = 123;\n"
                 "//--></script></body></html>"));
 
-  // Tests to cover more of the state machine inside MaybeSkipHtmlComment.
-  EXPECT_EQ(SniffingResult::kNo, CORB::SniffForHTML("<!-- -/* --><html>"));
-  EXPECT_EQ(SniffingResult::kNo, CORB::SniffForHTML("<!-- --/* --><html>"));
-  EXPECT_EQ(SniffingResult::kYes, CORB::SniffForHTML("<!----><html>"));
+  // Tests to cover more of MaybeSkipHtmlComment.
+  EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML("<!-- -/* --><html>"));
+  EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML("<!-- --/* --><html>"));
+  EXPECT_EQ(SniffingResult::kYes, CORB::SniffForHTML("<!-- -/* -->\n<html>"));
+  EXPECT_EQ(SniffingResult::kYes, CORB::SniffForHTML("<!-- --/* -->\n<html>"));
+  EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML("<!----> <html>"));
+  EXPECT_EQ(SniffingResult::kYes, CORB::SniffForHTML("<!---->\n<html>"));
+  EXPECT_EQ(SniffingResult::kYes, CORB::SniffForHTML("<!---->\r<html>"));
   EXPECT_EQ(SniffingResult::kYes,
-            CORB::SniffForHTML("<!-- ---/--> <html><body>"));
+            CORB::SniffForHTML("<!-- ---/-->\n<html><body>"));
+
+  // HTML spec only allows *ASCII* whitespace before the first html element.
+  // See also https://html.spec.whatwg.org/multipage/syntax.html and
+  // https://infra.spec.whatwg.org/#ascii-whitespace.
+  EXPECT_EQ(SniffingResult::kNo, CORB::SniffForHTML("<!---->\u2028<html>"));
+  EXPECT_EQ(SniffingResult::kNo, CORB::SniffForHTML("<!---->\u2029<html>"));
 
   // Commented out html tag followed by non-html (" x").
-  StringPiece commented_out_html_tag_data("<!-- <html> <?xml> \n<html>--> x");
+  StringPiece commented_out_html_tag_data("<!-- <html> <?xml> \n<html>-->\nx");
   EXPECT_EQ(SniffingResult::kNo,
             CrossOriginReadBlocking::SniffForHTML(commented_out_html_tag_data));
 
@@ -115,7 +125,8 @@
   EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML(""));
   EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML("<!"));
   EXPECT_EQ(SniffingResult::kMaybe, CORB::SniffForHTML("<!-- unterminated..."));
-  EXPECT_EQ(SniffingResult::kNo, CORB::SniffForHTML("<!-- /* js "));
+  EXPECT_EQ(SniffingResult::kMaybe,
+            CORB::SniffForHTML("<!-- blah --> <html> no newline yet"));
 }
 
 TEST(CrossOriginReadBlockingTest, SniffForXML) {
diff --git a/services/network/mojo_socket_test_util.cc b/services/network/mojo_socket_test_util.cc
new file mode 100644
index 0000000..2c583f2
--- /dev/null
+++ b/services/network/mojo_socket_test_util.cc
@@ -0,0 +1,50 @@
+// 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 "services/network/mojo_socket_test_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+
+TestSocketObserver::TestSocketObserver() : binding_(this) {}
+
+TestSocketObserver::~TestSocketObserver() {
+  EXPECT_EQ(net::OK, read_error_);
+  EXPECT_EQ(net::OK, write_error_);
+}
+
+mojom::SocketObserverPtr TestSocketObserver::GetObserverPtr() {
+  DCHECK(!binding_);
+
+  mojom::SocketObserverPtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
+int TestSocketObserver::WaitForReadError() {
+  read_loop_.Run();
+  int error = read_error_;
+  read_error_ = net::OK;
+  return error;
+}
+
+int TestSocketObserver::WaitForWriteError() {
+  write_loop_.Run();
+  int error = write_error_;
+  write_error_ = net::OK;
+  return error;
+}
+
+void TestSocketObserver::OnReadError(int net_error) {
+  read_error_ = net_error;
+  read_loop_.Quit();
+}
+
+void TestSocketObserver::OnWriteError(int net_error) {
+  write_error_ = net_error;
+  write_loop_.Quit();
+}
+
+}  // namespace network
diff --git a/services/network/mojo_socket_test_util.h b/services/network/mojo_socket_test_util.h
new file mode 100644
index 0000000..c045101
--- /dev/null
+++ b/services/network/mojo_socket_test_util.h
@@ -0,0 +1,45 @@
+// 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 SERVICES_NETWORK_MOJO_SOCKET_TEST_UTIL_H_
+#define SERVICES_NETWORK_MOJO_SOCKET_TEST_UTIL_H_
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/mojom/tcp_socket.mojom.h"
+
+namespace network {
+
+// A mojom::SocketObserver implementation used in tests.
+class TestSocketObserver : public mojom::SocketObserver {
+ public:
+  TestSocketObserver();
+  ~TestSocketObserver() override;
+
+  // Returns a mojo pointer. This can only be called once.
+  mojom::SocketObserverPtr GetObserverPtr();
+
+  // Waits for Read and Write error. Returns the error observed.
+  int WaitForReadError();
+  int WaitForWriteError();
+
+ private:
+  // mojom::SocketObserver implementation.
+  void OnReadError(int net_error) override;
+  void OnWriteError(int net_error) override;
+
+  int read_error_ = net::OK;
+  int write_error_ = net::OK;
+  base::RunLoop read_loop_;
+  base::RunLoop write_loop_;
+  mojo::Binding<mojom::SocketObserver> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSocketObserver);
+};
+
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_MOJO_SOCKET_TEST_UTIL_H_
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 539af82..f0d7dbf8d 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -9,13 +9,20 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/optional.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_traits.h"
+#include "build/build_config.h"
+#include "components/certificate_transparency/chrome_ct_policy_enforcer.h"
 #include "components/certificate_transparency/ct_policy_manager.h"
+#include "components/certificate_transparency/features.h"
+#include "components/certificate_transparency/sth_distributor.h"
+#include "components/certificate_transparency/sth_reporter.h"
+#include "components/certificate_transparency/tree_state_tracker.h"
 #include "components/cookie_config/cookie_store_util.h"
 #include "components/network_session_configurator/browser/network_session_configurator.h"
 #include "components/network_session_configurator/common/network_switches.h"
@@ -24,6 +31,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/ct_log_verifier.h"
+#include "net/cert/multi_log_ct_verifier.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/mapped_host_resolver.h"
@@ -210,8 +220,15 @@
   url_request_context_owner_ = ApplyContextParamsToBuilder(
       builder.get(), params_.get(), network_service->quic_disabled(),
       network_service->net_log(), network_service->network_quality_estimator(),
+      network_service_->sth_reporter(), &ct_tree_tracker_,
       &user_agent_settings_);
   url_request_context_ = url_request_context_owner_.url_request_context.get();
+  if (ct_tree_tracker_ && network_service_->sth_reporter()) {
+    url_request_context_->cert_transparency_verifier()->SetObserver(
+        ct_tree_tracker_.get());
+    network_service_->sth_reporter()->RegisterObserver(ct_tree_tracker_.get());
+  }
+
   network_service_->RegisterNetworkContext(this);
   cookie_manager_ =
       std::make_unique<CookieManager>(url_request_context_->cookie_store());
@@ -249,6 +266,17 @@
     url_request_context_->transport_security_state()->SetRequireCTDelegate(
         nullptr);
   }
+
+  if (url_request_context_ &&
+      url_request_context_->cert_transparency_verifier()) {
+    url_request_context_->cert_transparency_verifier()->SetObserver(nullptr);
+  }
+
+  if (network_service_ && network_service_->sth_reporter() &&
+      ct_tree_tracker_) {
+    network_service_->sth_reporter()->UnregisterObserver(
+        ct_tree_tracker_.get());
+  }
 }
 
 std::unique_ptr<NetworkContext> NetworkContext::CreateForTesting() {
@@ -415,13 +443,23 @@
   }
 
   // |network_service_| may be nullptr in tests.
-  return ApplyContextParamsToBuilder(
+  auto result = ApplyContextParamsToBuilder(
       &builder, network_context_params,
       network_service_ ? network_service_->quic_disabled() : false,
       network_service_ ? network_service_->net_log() : nullptr,
       network_service_ ? network_service_->network_quality_estimator()
                        : nullptr,
-      &user_agent_settings_);
+      network_service_ ? network_service_->sth_reporter() : nullptr,
+      &ct_tree_tracker_, &user_agent_settings_);
+
+  if (ct_tree_tracker_ && network_service_ &&
+      network_service_->sth_reporter()) {
+    result.url_request_context->cert_transparency_verifier()->SetObserver(
+        ct_tree_tracker_.get());
+    network_service_->sth_reporter()->RegisterObserver(ct_tree_tracker_.get());
+  }
+
+  return result;
 }
 
 URLRequestContextOwner NetworkContext::ApplyContextParamsToBuilder(
@@ -430,6 +468,9 @@
     bool quic_disabled,
     net::NetLog* net_log,
     net::NetworkQualityEstimator* network_quality_estimator,
+    certificate_transparency::STHReporter* sth_reporter,
+    std::unique_ptr<certificate_transparency::TreeStateTracker>*
+        out_ct_tree_tracker,
     net::StaticHttpUserAgentSettings** out_http_user_agent_settings) {
   if (net_log)
     builder->set_net_log(net_log);
@@ -534,6 +575,11 @@
       base::FeatureList::IsEnabled(features::kNetworkErrorLogging));
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+  if (network_context_params->enforce_chrome_ct_policy) {
+    builder->set_ct_policy_enforcer(
+        std::make_unique<certificate_transparency::ChromeCTPolicyEnforcer>());
+  }
+
   net::HttpNetworkSession::Params session_params;
   bool is_quic_force_disabled = false;
   if (quic_disabled)
@@ -553,7 +599,38 @@
                          -> std::unique_ptr<net::HttpTransactionFactory> {
         return std::make_unique<ThrottlingNetworkTransactionFactory>(session);
       }));
-  return URLRequestContextOwner(std::move(pref_service), builder->Build());
+
+  std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs;
+  if (!network_context_params->ct_logs.empty()) {
+    for (const auto& log : network_context_params->ct_logs) {
+      scoped_refptr<const net::CTLogVerifier> log_verifier =
+          net::CTLogVerifier::Create(log->public_key, log->name,
+                                     log->dns_api_endpoint);
+      if (!log_verifier) {
+        // TODO: Signal bad configuration (such as bad key).
+        continue;
+      }
+      ct_logs.push_back(std::move(log_verifier));
+    }
+    auto ct_verifier = std::make_unique<net::MultiLogCTVerifier>();
+    ct_verifier->AddLogs(ct_logs);
+    builder->set_ct_verifier(std::move(ct_verifier));
+  }
+
+  auto result =
+      URLRequestContextOwner(std::move(pref_service), builder->Build());
+
+#if !defined(OS_IOS)
+  if (base::FeatureList::IsEnabled(certificate_transparency::kCTLogAuditing) &&
+      out_ct_tree_tracker && sth_reporter && !ct_logs.empty()) {
+    net::URLRequestContext* context = result.url_request_context.get();
+    *out_ct_tree_tracker =
+        std::make_unique<certificate_transparency::TreeStateTracker>(
+            ct_logs, context->host_resolver(), net_log);
+  }
+#endif
+
+  return result;
 }
 
 void NetworkContext::DestroyURLLoaderFactory(
@@ -755,7 +832,7 @@
     const net::AddressList& remote_addr_list,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
     mojom::TCPConnectedSocketRequest request,
-    mojom::TCPConnectedSocketObserverPtr observer,
+    mojom::SocketObserverPtr observer,
     CreateTCPConnectedSocketCallback callback) {
   socket_factory_->CreateTCPConnectedSocket(
       local_addr, remote_addr_list,
@@ -803,6 +880,7 @@
   // Throw away old version; since this is a a browser test, we don't
   // need to restore the old state.
   cache->SetHttpNetworkTransactionFactoryForTesting(std::move(factory));
+
   std::move(callback).Run();
 }
 
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 0267145..02d19a5 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -41,7 +41,9 @@
 
 namespace certificate_transparency {
 class CTPolicyManager;
-}
+class TreeStateTracker;
+class STHReporter;
+}  // namespace certificate_transparency
 
 namespace network {
 class NetworkService;
@@ -153,7 +155,7 @@
       const net::AddressList& remote_addr_list,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       mojom::TCPConnectedSocketRequest request,
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       CreateTCPConnectedSocketCallback callback) override;
   void CreateWebSocket(mojom::WebSocketRequest request,
                        int32_t process_id,
@@ -185,6 +187,9 @@
       bool quic_disabled,
       net::NetLog* net_log,
       net::NetworkQualityEstimator* network_quality_estimator,
+      certificate_transparency::STHReporter* sth_reporter,
+      std::unique_ptr<certificate_transparency::TreeStateTracker>*
+          out_tree_state_tracker,
       net::StaticHttpUserAgentSettings** out_http_user_agent_settings);
 
   // Destroys the specified URLLoaderFactory.  Called by the URLLoaderFactory
@@ -248,6 +253,7 @@
   constexpr static bool enable_resource_scheduler_ = true;
 
   std::unique_ptr<certificate_transparency::CTPolicyManager> ct_policy_manager_;
+  std::unique_ptr<certificate_transparency::TreeStateTracker> ct_tree_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkContext);
 };
diff --git a/services/network/network_context_cert_transparency_unittest.cc b/services/network/network_context_cert_transparency_unittest.cc
new file mode 100644
index 0000000..f294d5a1
--- /dev/null
+++ b/services/network/network_context_cert_transparency_unittest.cc
@@ -0,0 +1,436 @@
+// 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 <memory>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "base/test/gtest_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/certificate_transparency/features.h"
+#include "components/certificate_transparency/single_tree_tracker.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "net/base/address_list.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/ct_known_logs.h"
+#include "net/cert/ct_policy_status.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/ct_verifier.h"
+#include "net/cert/mock_cert_verifier.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
+#include "net/cert/x509_certificate.h"
+#include "net/dns/host_cache.h"
+#include "net/dns/host_resolver.h"
+#include "net/proxy_resolution/proxy_config_with_annotation.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/ct_test_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/test_data_directory.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_context.h"
+#include "services/network/network_context.h"
+#include "services/network/network_service.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/ct_log_info.mojom.h"
+#include "services/network/public/mojom/network_service.mojom.h"
+#include "services/network/public/mojom/proxy_config.mojom.h"
+#include "services/network/test/test_url_loader_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+
+namespace {
+
+mojom::NetworkContextParamsPtr CreateContextParams() {
+  mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
+
+  // Use a fixed proxy config, to avoid dependencies on local network
+  // configuration.
+  params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
+
+  // Configure Certificate Transparency for the context.
+  // TODO(robpercival): https://crbug.com/839612 - Use test logs for
+  // integration tests rather than production logs.
+  const char kPilotKey[] =
+      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
+      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3"
+      "\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18"
+      "\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a"
+      "\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd"
+      "\x1f\x04\x10\x41\x4c\xa0";
+  const char kAviatorKey[] =
+      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
+      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xf4\xcc\x69\xb2\xe4\x0e"
+      "\x90\xa3\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b"
+      "\x40\x50\x07\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34"
+      "\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85"
+      "\x3b\x0d\xf7\x1f\x3f\xe9";
+  const char kDigiCertKey[] =
+      "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86"
+      "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82"
+      "\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9"
+      "\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf"
+      "\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91"
+      "\xeb\x38\x24\x40\xac\xe8";
+  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
+      std::string(kPilotKey, base::size(kPilotKey) - 1), "Google 'Pilot' Log",
+      "pilot.ct.invalid"));
+  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
+      std::string(kAviatorKey, base::size(kAviatorKey) - 1),
+      "Google 'Aviator' Log", "aviator.ct.invalid"));
+  params->ct_logs.push_back(network::mojom::CTLogInfo::New(
+      std::string(kDigiCertKey, base::size(kDigiCertKey) - 1),
+      "DigiCert Log Server", "digicert.ct.invalid"));
+
+  return params;
+}
+
+// TODO(robpercival): https://crbug.com/839612 - Make it easier to use a test
+// cert that is not so tightly-coupled to production logs and STHs.
+scoped_refptr<net::X509Certificate> GetCTCertForTesting() {
+  base::ScopedAllowBlockingForTesting allow_blocking_for_loading_cert;
+  return net::CreateCertificateChainFromFile(
+      net::GetTestCertsDirectory(), "comodo-chain.pem",
+      net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+}
+
+// The number of valid SCTs in |GetCTCertForTesting| from logs configured in
+// |CreateContextParams()|.
+const int kNumSCTs = 3;
+
+// Decodes a base64-encoded "DigitallySigned" TLS struct into |*sig_out|.
+// See https://tools.ietf.org/html/rfc5246#section-4.7.
+// |sig_out| must not be null.
+bool DecodeDigitallySigned(base::StringPiece base64_data,
+                           net::ct::DigitallySigned* sig_out) {
+  std::string data;
+  if (!base::Base64Decode(base64_data, &data))
+    return false;
+
+  base::StringPiece data_ptr = data;
+  if (!net::ct::DecodeDigitallySigned(&data_ptr, sig_out))
+    return false;
+
+  return true;
+}
+
+// Populates |*sth_out| with the given information.
+// |sth_out| must not be null.
+bool BuildSignedTreeHead(base::Time timestamp,
+                         uint64_t tree_size,
+                         base::StringPiece root_hash_base64,
+                         base::StringPiece signature_base64,
+                         base::StringPiece log_id_base64,
+                         net::ct::SignedTreeHead* sth_out) {
+  sth_out->version = net::ct::SignedTreeHead::V1;
+  sth_out->timestamp = timestamp;
+  sth_out->tree_size = tree_size;
+
+  std::string root_hash;
+  if (!base::Base64Decode(root_hash_base64, &root_hash)) {
+    return false;
+  }
+  root_hash.copy(sth_out->sha256_root_hash, net::ct::kSthRootHashLength);
+
+  return DecodeDigitallySigned(signature_base64, &sth_out->signature) &&
+         base::Base64Decode(log_id_base64, &sth_out->log_id);
+}
+
+TEST(NetworkContextCertTransparencyAuditingDisabledTest,
+     SCTsAreNotCheckedForInclusion) {
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::IO);
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      certificate_transparency::kCTLogAuditing);
+
+  std::unique_ptr<NetworkService> network_service =
+      NetworkService::CreateForTesting();
+
+  // Override the CertVerifier, so that a 'real' cert can be simulated being
+  // returned by the net::TestServer. This should be done before creating the
+  // context.
+  net::MockCertVerifier mock_cert_verifier;
+  NetworkContext::SetCertVerifierForTesting(&mock_cert_verifier);
+  base::ScopedClosureRunner cleanup(base::BindOnce(
+      [] { NetworkContext::SetCertVerifierForTesting(nullptr); }));
+
+  mojom::NetworkContextParamsPtr context_params = CreateContextParams();
+  mojom::NetworkContextPtr network_context_ptr;
+  std::unique_ptr<NetworkContext> network_context =
+      std::make_unique<NetworkContext>(network_service.get(),
+                                       mojo::MakeRequest(&network_context_ptr),
+                                       std::move(context_params));
+
+  // Certificate Transparency should be configured, but there should be
+  // nothing listening for SCTs (such as the NetworkContext's ct_tree_tracker_).
+  ASSERT_TRUE(
+      network_context->url_request_context()->cert_transparency_verifier());
+  EXPECT_FALSE(network_context->url_request_context()
+                   ->cert_transparency_verifier()
+                   ->GetObserver());
+
+  // Provide an STH from Google's Pilot log that can be used to prove
+  // inclusion for an SCT later in the test.
+  net::ct::SignedTreeHead pilot_sth;
+  ASSERT_TRUE(BuildSignedTreeHead(
+      base::Time::FromJsTime(1512419914170), 181871752,
+      "bvgljSy3Yg32Y6J8qL5WmUA3jn2WnOrEFDqxD0AxUvs=",
+      "BAMARjBEAiAwEXve2RBk3XkUR+6nACSETTgzKFaEeginxuj5U9BI/"
+      "wIgBPuQS5ACxsro6TtpY4bQyE6WlMdcSMiMd/SSGraOBOg=",
+      "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", &pilot_sth));
+  network_service->UpdateSignedTreeHead(pilot_sth);
+
+  // Provide an STH from Google's Aviator log that is not recent enough to
+  // prove inclusion for an SCT later in the test.
+  net::ct::SignedTreeHead aviator_sth;
+  ASSERT_TRUE(BuildSignedTreeHead(
+      base::Time::FromJsTime(1442652106945), 8502329,
+      "bfG+gWZcHl9fqtNo0Z/uggs8E5YqGOtJQ0Z5zVZDRxI=",
+      "BAMARjBEAiA6elcNQoShmKLHj/"
+      "IA649UIbaQtWJEpj0Eot0q7G6fEgIgYChb7U6Reuvt0nO5PionH+3UciOxKV3Cy8/"
+      "eq59lSYY=",
+      "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=", &aviator_sth));
+  network_service->UpdateSignedTreeHead(aviator_sth);
+
+  // Start a test server on "localhost" and configure connections to it to
+  // simulate using a real certificate.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  ASSERT_TRUE(https_server.Start());
+
+  // Flush any pending tasks, in particular, any configuration updates to
+  // the network or DNS configuration. This is because SCTs are only checked
+  // if DNS was used and the network config has not changed, for privacy
+  // reasons.
+  scoped_task_environment.RunUntilIdle();
+
+  // Configure "localhost" to be treated as if it went through DNS. This
+  // modifies the HostCache directly to simulate it being cached, rather than
+  // indirecting through a scoped HostResolverProc, as queries that use
+  // HostResolverProcs are treated as SOURCE_UNKNOWN, rather than SOURCE_DNS.
+  net::AddressList address_list;
+  ASSERT_TRUE(https_server.GetAddressList(&address_list));
+
+  net::HostCache* host_cache =
+      network_context->url_request_context()->host_resolver()->GetHostCache();
+  ASSERT_TRUE(host_cache);
+  host_cache->Set(
+      net::HostCache::Key("localhost", net::ADDRESS_FAMILY_UNSPECIFIED, 0),
+      net::HostCache::Entry(net::OK, address_list,
+                            net::HostCache::Entry::SOURCE_DNS),
+      base::TimeTicks::Now(), base::TimeDelta());
+
+  // This certificate contains 3 SCTs and fulfills the Chrome CT policy.
+  // Simulate it being trusted by a known root, as otherwise CT is skipped for
+  // private roots.
+  net::CertVerifyResult verify_result;
+  verify_result.is_issued_by_known_root = true;
+  verify_result.cert_status = 0;
+  verify_result.verified_cert = GetCTCertForTesting();
+  ASSERT_TRUE(verify_result.verified_cert);
+  mock_cert_verifier.AddResultForCert(https_server.GetCertificate(),
+                                      verify_result, net::OK);
+
+  ResourceRequest request;
+  request.url = https_server.GetURL("localhost", "/");
+
+  mojom::URLLoaderFactoryPtr loader_factory;
+  network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
+                                          0);
+
+  base::HistogramTester histograms;
+  mojom::URLLoaderPtr loader;
+  TestURLLoaderClient client;
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client.CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client.RunUntilResponseReceived();
+  EXPECT_TRUE(client.has_received_response());
+  EXPECT_TRUE(client.has_received_completion());
+
+  // Expect only a single connection.
+  ASSERT_EQ(histograms.GetBucketCount("Net.SSL_Connection_Error", net::OK), 1);
+
+  // Expect 3 SCTs in this connection.
+  EXPECT_THAT(histograms.GetBucketCount(
+                  "Net.CertificateTransparency.SCTsPerConnection", kNumSCTs),
+              1);
+
+  // Expect that the SCTs were embedded in the certificate.
+  EXPECT_THAT(
+      histograms.GetBucketCount(
+          "Net.CertificateTransparency.SCTOrigin",
+          static_cast<int>(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)),
+      kNumSCTs);
+
+  // No SCTs should be eligible for inclusion checking, as inclusion checking
+  // is disabled.
+  histograms.ExpectTotalCount(
+      "Net.CertificateTransparency.CanInclusionCheckSCT", 0);
+}
+
+TEST(NetworkContextCertTransparencyAuditingEnabledTest,
+     SCTsAreCheckedForInclusion) {
+  base::test::ScopedTaskEnvironment scoped_task_environment(
+      base::test::ScopedTaskEnvironment::MainThreadType::IO);
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      certificate_transparency::kCTLogAuditing);
+
+  std::unique_ptr<NetworkService> network_service =
+      NetworkService::CreateForTesting();
+
+  // Override the CertVerifier, so that a 'real' cert can be simulated being
+  // returned by the net::TestServer. This should be done before creating the
+  // context.
+  net::MockCertVerifier mock_cert_verifier;
+  NetworkContext::SetCertVerifierForTesting(&mock_cert_verifier);
+  base::ScopedClosureRunner cleanup(base::BindOnce(
+      [] { NetworkContext::SetCertVerifierForTesting(nullptr); }));
+
+  mojom::NetworkContextParamsPtr context_params = CreateContextParams();
+  mojom::NetworkContextPtr network_context_ptr;
+  std::unique_ptr<NetworkContext> network_context =
+      std::make_unique<NetworkContext>(network_service.get(),
+                                       mojo::MakeRequest(&network_context_ptr),
+                                       std::move(context_params));
+
+  // Certificate Transparency should be configured, but there should be
+  // nothing listening for SCTs (such as the NetworkContext's ct_tree_tracker_).
+  ASSERT_TRUE(
+      network_context->url_request_context()->cert_transparency_verifier());
+  EXPECT_TRUE(network_context->url_request_context()
+                  ->cert_transparency_verifier()
+                  ->GetObserver());
+
+  // Provide an STH from Google's Pilot log that can be used to prove
+  // inclusion for an SCT later in the test.
+  net::ct::SignedTreeHead pilot_sth;
+  ASSERT_TRUE(BuildSignedTreeHead(
+      base::Time::FromJsTime(1512419914170), 181871752,
+      "bvgljSy3Yg32Y6J8qL5WmUA3jn2WnOrEFDqxD0AxUvs=",
+      "BAMARjBEAiAwEXve2RBk3XkUR+6nACSETTgzKFaEeginxuj5U9BI/"
+      "wIgBPuQS5ACxsro6TtpY4bQyE6WlMdcSMiMd/SSGraOBOg=",
+      "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", &pilot_sth));
+  network_service->UpdateSignedTreeHead(pilot_sth);
+
+  // Provide an STH from Google's Aviator log that is not recent enough to
+  // prove inclusion for an SCT later in the test.
+  net::ct::SignedTreeHead aviator_sth;
+  ASSERT_TRUE(BuildSignedTreeHead(
+      base::Time::FromJsTime(1442652106945), 8502329,
+      "bfG+gWZcHl9fqtNo0Z/uggs8E5YqGOtJQ0Z5zVZDRxI=",
+      "BAMARjBEAiA6elcNQoShmKLHj/"
+      "IA649UIbaQtWJEpj0Eot0q7G6fEgIgYChb7U6Reuvt0nO5PionH+3UciOxKV3Cy8/"
+      "eq59lSYY=",
+      "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=", &aviator_sth));
+  network_service->UpdateSignedTreeHead(aviator_sth);
+
+  // Start a test server on "localhost" and configure connections to it to
+  // simulate using a real certificate.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  ASSERT_TRUE(https_server.Start());
+
+  // Flush any pending tasks, in particular, any configuration updates to
+  // the network or DNS configuration. This is because SCTs are only checked
+  // if DNS was used and the network config has not changed, for privacy
+  // reasons.
+  scoped_task_environment.RunUntilIdle();
+
+  // Configure "localhost" to be treated as if it went through DNS. This
+  // modifies the HostCache directly to simulate it being cached, rather than
+  // indirecting through a scoped HostResolverProc, as queries that use
+  // HostResolverProcs are treated as SOURCE_UNKNOWN, rather than SOURCE_DNS.
+  net::AddressList address_list;
+  ASSERT_TRUE(https_server.GetAddressList(&address_list));
+
+  net::HostCache* host_cache =
+      network_context->url_request_context()->host_resolver()->GetHostCache();
+  ASSERT_TRUE(host_cache);
+  host_cache->Set(
+      net::HostCache::Key("localhost", net::ADDRESS_FAMILY_UNSPECIFIED, 0),
+      net::HostCache::Entry(net::OK, address_list,
+                            net::HostCache::Entry::SOURCE_DNS),
+      base::TimeTicks::Now(), base::TimeDelta());
+
+  // This certificate contains 3 SCTs and fulfills the Chrome CT policy.
+  // Simulate it being trusted by a known root, as otherwise CT is skipped for
+  // private roots.
+  net::CertVerifyResult verify_result;
+  verify_result.is_issued_by_known_root = true;
+  verify_result.cert_status = 0;
+  verify_result.verified_cert = GetCTCertForTesting();
+  ASSERT_TRUE(verify_result.verified_cert);
+  mock_cert_verifier.AddResultForCert(https_server.GetCertificate(),
+                                      verify_result, net::OK);
+
+  base::HistogramTester histograms;
+
+  ResourceRequest request;
+  request.url = https_server.GetURL("localhost", "/");
+
+  mojom::URLLoaderFactoryPtr loader_factory;
+  network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
+                                          0);
+
+  mojom::URLLoaderPtr loader;
+  TestURLLoaderClient client;
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client.CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client.RunUntilResponseReceived();
+  EXPECT_TRUE(client.has_received_response());
+  EXPECT_TRUE(client.has_received_completion());
+
+  // Expect only a single connection.
+  ASSERT_EQ(histograms.GetBucketCount("Net.SSL_Connection_Error", net::OK), 1);
+
+  // Expect 3 SCTs in this connection.
+  EXPECT_THAT(histograms.GetBucketCount(
+                  "Net.CertificateTransparency.SCTsPerConnection", kNumSCTs),
+              1);
+
+  // Expect that the SCTs were embedded in the certificate.
+  EXPECT_THAT(
+      histograms.GetBucketCount(
+          "Net.CertificateTransparency.SCTOrigin",
+          static_cast<int>(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED)),
+      kNumSCTs);
+
+  // The Pilot SCT should be eligible for inclusion checking, because a recent
+  // enough Pilot STH is available.
+  histograms.ExpectBucketCount(
+      "Net.CertificateTransparency.CanInclusionCheckSCT",
+      certificate_transparency::CAN_BE_CHECKED, 1);
+  // The Aviator SCT should not be eligible for inclusion checking, because
+  // there is not a recent enough Aviator STH available.
+  histograms.ExpectBucketCount(
+      "Net.CertificateTransparency.CanInclusionCheckSCT",
+      certificate_transparency::NEWER_STH_REQUIRED, 1);
+  // The DigiCert SCT should not be eligible for inclusion checking, because
+  // there is no DigiCert STH available.
+  histograms.ExpectBucketCount(
+      "Net.CertificateTransparency.CanInclusionCheckSCT",
+      certificate_transparency::VALID_STH_REQUIRED, 1);
+}
+
+}  // namespace
+
+}  // namespace network
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index ef667fb..2657d3e1 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -12,10 +12,15 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/values.h"
 #include "build/build_config.h"
+#include "components/certificate_transparency/sth_distributor.h"
+#include "components/certificate_transparency/sth_observer.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/logging_network_change_observer.h"
 #include "net/base/network_change_notifier.h"
+#include "net/cert/ct_log_response_parser.h"
+#include "net/cert/signed_tree_head.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/mapped_host_resolver.h"
 #include "net/log/net_log.h"
@@ -153,6 +158,8 @@
   host_resolver_ = CreateHostResolver();
 
   network_usage_accumulator_ = std::make_unique<NetworkUsageAccumulator>();
+  sth_distributor_ =
+      std::make_unique<certificate_transparency::STHDistributor>();
 }
 
 NetworkService::~NetworkService() {
@@ -257,6 +264,14 @@
   std::move(callback).Run(network_usage_accumulator_->GetTotalNetworkUsages());
 }
 
+void NetworkService::UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) {
+  sth_distributor_->NewSTHObserved(sth);
+}
+
+certificate_transparency::STHReporter* NetworkService::sth_reporter() {
+  return sth_distributor_.get();
+}
+
 void NetworkService::OnBindInterface(
     const service_manager::BindSourceInfo& source_info,
     const std::string& interface_name,
diff --git a/services/network/network_service.h b/services/network/network_service.h
index cd76b69..f48fd50 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -11,6 +11,7 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/log/net_log.h"
 #include "services/network/keepalive_statistics_recorder.h"
@@ -28,6 +29,11 @@
 class URLRequestContext;
 }  // namespace net
 
+namespace certificate_transparency {
+class STHDistributor;
+class STHReporter;
+}  // namespace certificate_transparency
+
 namespace network {
 
 class NetworkContext;
@@ -102,6 +108,7 @@
       mojom::NetworkChangeManagerRequest request) override;
   void GetTotalNetworkUsages(
       mojom::NetworkService::GetTotalNetworkUsagesCallback callback) override;
+  void UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) override;
 
   bool quic_disabled() const { return quic_disabled_; }
   bool HasRawHeadersAccess(uint32_t process_id) const;
@@ -119,6 +126,8 @@
     return network_usage_accumulator_.get();
   }
 
+  certificate_transparency::STHReporter* sth_reporter();
+
  private:
   // service_manager::Service implementation.
   void OnBindInterface(const service_manager::BindSourceInfo& source_info,
@@ -160,6 +169,8 @@
 
   bool quic_disabled_ = false;
 
+  std::unique_ptr<certificate_transparency::STHDistributor> sth_distributor_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkService);
 };
 
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index f22f20b..5b1c347 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -107,6 +107,7 @@
   deps = [
     "//base",
     "//ipc",
+    "//mojo/public/mojom/base",
     "//net",
     "//services/network/public/mojom:mojom_shared",
   ]
@@ -129,10 +130,12 @@
     "cors/cors_unittest.cc",
     "cors/preflight_cache_unittest.cc",
     "cors/preflight_result_unittest.cc",
+    "digitally_signed_mojom_traits_unittest.cc",
     "mutable_network_traffic_annotation_tag_mojom_traits_unittest.cc",
     "mutable_partial_network_traffic_annotation_tag_mojom_traits_unittest.cc",
     "network_mojom_traits_unittest.cc",
     "proxy_config_mojom_traits_unittest.cc",
+    "signed_tree_head_mojom_traits_unittest.cc",
     "simple_url_loader_unittest.cc",
   ]
 
diff --git a/services/network/public/cpp/digitally_signed.typemap b/services/network/public/cpp/digitally_signed.typemap
new file mode 100644
index 0000000..f18eaad
--- /dev/null
+++ b/services/network/public/cpp/digitally_signed.typemap
@@ -0,0 +1,19 @@
+# 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.
+
+mojom = "//services/network/public/mojom/digitally_signed.mojom"
+public_headers = [ "//net/cert/signed_certificate_timestamp.h" ]
+traits_headers =
+    [ "//services/network/public/cpp/digitally_signed_mojom_traits.h" ]
+sources = [
+  "//services/network/public/cpp/digitally_signed_mojom_traits.cc",
+]
+type_mappings = [
+  "network.mojom.HashAlgorithm=net::ct::DigitallySigned::HashAlgorithm",
+  "network.mojom.SignatureAlgorithm=net::ct::DigitallySigned::SignatureAlgorithm",
+  "network.mojom.DigitallySigned=net::ct::DigitallySigned",
+]
+public_deps = [
+  "//net",
+]
diff --git a/services/network/public/cpp/digitally_signed_mojom_traits.cc b/services/network/public/cpp/digitally_signed_mojom_traits.cc
new file mode 100644
index 0000000..b36efe6
--- /dev/null
+++ b/services/network/public/cpp/digitally_signed_mojom_traits.cc
@@ -0,0 +1,30 @@
+// 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 "services/network/public/cpp/digitally_signed_mojom_traits.h"
+
+#include <vector>
+
+namespace mojo {
+
+// static
+bool StructTraits<network::mojom::DigitallySignedDataView,
+                  net::ct::DigitallySigned>::
+    Read(network::mojom::DigitallySignedDataView data,
+         net::ct::DigitallySigned* out) {
+  std::vector<uint8_t> signature_data;
+  if (!data.ReadHashAlgorithm(&out->hash_algorithm) ||
+      !data.ReadSignatureAlgorithm(&out->signature_algorithm) ||
+      !data.ReadSignature(&signature_data)) {
+    return false;
+  }
+  if (signature_data.empty())
+    return false;
+  out->signature_data.assign(
+      reinterpret_cast<const char*>(signature_data.data()),
+      signature_data.size());
+  return true;
+}
+
+}  // namespace mojo
diff --git a/services/network/public/cpp/digitally_signed_mojom_traits.h b/services/network/public/cpp/digitally_signed_mojom_traits.h
new file mode 100644
index 0000000..c01712a
--- /dev/null
+++ b/services/network/public/cpp/digitally_signed_mojom_traits.h
@@ -0,0 +1,133 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_DIGITALLY_SIGNED_MOJOM_TRAITS_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_DIGITALLY_SIGNED_MOJOM_TRAITS_H_
+
+#include "base/containers/span.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "services/network/public/mojom/digitally_signed.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<network::mojom::HashAlgorithm,
+                  net::ct::DigitallySigned::HashAlgorithm> {
+  static network::mojom::HashAlgorithm ToMojom(
+      net::ct::DigitallySigned::HashAlgorithm input) {
+    switch (input) {
+      case net::ct::DigitallySigned::HASH_ALGO_NONE:
+        return network::mojom::HashAlgorithm::HASH_ALGO_NONE;
+      case net::ct::DigitallySigned::HASH_ALGO_MD5:
+        return network::mojom::HashAlgorithm::HASH_ALGO_MD5;
+      case net::ct::DigitallySigned::HASH_ALGO_SHA1:
+        return network::mojom::HashAlgorithm::HASH_ALGO_SHA1;
+      case net::ct::DigitallySigned::HASH_ALGO_SHA224:
+        return network::mojom::HashAlgorithm::HASH_ALGO_SHA224;
+      case net::ct::DigitallySigned::HASH_ALGO_SHA256:
+        return network::mojom::HashAlgorithm::HASH_ALGO_SHA256;
+      case net::ct::DigitallySigned::HASH_ALGO_SHA384:
+        return network::mojom::HashAlgorithm::HASH_ALGO_SHA384;
+      case net::ct::DigitallySigned::HASH_ALGO_SHA512:
+        return network::mojom::HashAlgorithm::HASH_ALGO_SHA512;
+    }
+    NOTREACHED();
+    return network::mojom::HashAlgorithm::HASH_ALGO_NONE;
+  }
+
+  static bool FromMojom(network::mojom::HashAlgorithm input,
+                        net::ct::DigitallySigned::HashAlgorithm* output) {
+    switch (input) {
+      case network::mojom::HashAlgorithm::HASH_ALGO_NONE:
+        *output = net::ct::DigitallySigned::HASH_ALGO_NONE;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_MD5:
+        *output = net::ct::DigitallySigned::HASH_ALGO_MD5;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_SHA1:
+        *output = net::ct::DigitallySigned::HASH_ALGO_SHA1;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_SHA224:
+        *output = net::ct::DigitallySigned::HASH_ALGO_SHA224;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_SHA256:
+        *output = net::ct::DigitallySigned::HASH_ALGO_SHA256;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_SHA384:
+        *output = net::ct::DigitallySigned::HASH_ALGO_SHA384;
+        return true;
+      case network::mojom::HashAlgorithm::HASH_ALGO_SHA512:
+        *output = net::ct::DigitallySigned::HASH_ALGO_SHA512;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+template <>
+struct EnumTraits<network::mojom::SignatureAlgorithm,
+                  net::ct::DigitallySigned::SignatureAlgorithm> {
+  static network::mojom::SignatureAlgorithm ToMojom(
+      net::ct::DigitallySigned::SignatureAlgorithm input) {
+    switch (input) {
+      case net::ct::DigitallySigned::SIG_ALGO_ANONYMOUS:
+        return network::mojom::SignatureAlgorithm::SIG_ALGO_ANONYMOUS;
+      case net::ct::DigitallySigned::SIG_ALGO_RSA:
+        return network::mojom::SignatureAlgorithm::SIG_ALGO_RSA;
+      case net::ct::DigitallySigned::SIG_ALGO_DSA:
+        return network::mojom::SignatureAlgorithm::SIG_ALGO_DSA;
+      case net::ct::DigitallySigned::SIG_ALGO_ECDSA:
+        return network::mojom::SignatureAlgorithm::SIG_ALGO_ECDSA;
+    }
+    NOTREACHED();
+    return network::mojom::SignatureAlgorithm::SIG_ALGO_ANONYMOUS;
+  }
+
+  static bool FromMojom(network::mojom::SignatureAlgorithm input,
+                        net::ct::DigitallySigned::SignatureAlgorithm* output) {
+    switch (input) {
+      case network::mojom::SignatureAlgorithm::SIG_ALGO_ANONYMOUS:
+        *output = net::ct::DigitallySigned::SIG_ALGO_ANONYMOUS;
+        return true;
+      case network::mojom::SignatureAlgorithm::SIG_ALGO_RSA:
+        *output = net::ct::DigitallySigned::SIG_ALGO_RSA;
+        return true;
+      case network::mojom::SignatureAlgorithm::SIG_ALGO_DSA:
+        *output = net::ct::DigitallySigned::SIG_ALGO_DSA;
+        return true;
+      case network::mojom::SignatureAlgorithm::SIG_ALGO_ECDSA:
+        *output = net::ct::DigitallySigned::SIG_ALGO_ECDSA;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+template <>
+struct StructTraits<network::mojom::DigitallySignedDataView,
+                    net::ct::DigitallySigned> {
+  static net::ct::DigitallySigned::HashAlgorithm hash_algorithm(
+      const net::ct::DigitallySigned& obj) {
+    return obj.hash_algorithm;
+  }
+  static net::ct::DigitallySigned::SignatureAlgorithm signature_algorithm(
+      const net::ct::DigitallySigned& obj) {
+    return obj.signature_algorithm;
+  }
+  static base::span<const uint8_t> signature(
+      const net::ct::DigitallySigned& obj) {
+    return base::as_bytes(base::make_span(obj.signature_data));
+  }
+
+  static bool Read(network::mojom::DigitallySignedDataView obj,
+                   net::ct::DigitallySigned* out);
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_DIGITALLY_SIGNED_MOJOM_TRAITS_H_
diff --git a/services/network/public/cpp/digitally_signed_mojom_traits_unittest.cc b/services/network/public/cpp/digitally_signed_mojom_traits_unittest.cc
new file mode 100644
index 0000000..7272e5e
--- /dev/null
+++ b/services/network/public/cpp/digitally_signed_mojom_traits_unittest.cc
@@ -0,0 +1,69 @@
+// 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 "services/network/public/cpp/digitally_signed_mojom_traits.h"
+
+#include "base/test/gtest_util.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "services/network/public/mojom/digitally_signed.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+namespace {
+
+TEST(DigitallySignedTraitsTest, Roundtrips) {
+  for (auto sig_alg : {net::ct::DigitallySigned::SIG_ALGO_ANONYMOUS,
+                       net::ct::DigitallySigned::SIG_ALGO_RSA,
+                       net::ct::DigitallySigned::SIG_ALGO_DSA,
+                       net::ct::DigitallySigned::SIG_ALGO_ECDSA}) {
+    for (auto hash_alg : {net::ct::DigitallySigned::HASH_ALGO_NONE,
+                          net::ct::DigitallySigned::HASH_ALGO_MD5,
+                          net::ct::DigitallySigned::HASH_ALGO_SHA1,
+                          net::ct::DigitallySigned::HASH_ALGO_SHA224,
+                          net::ct::DigitallySigned::HASH_ALGO_SHA256,
+                          net::ct::DigitallySigned::HASH_ALGO_SHA384,
+                          net::ct::DigitallySigned::HASH_ALGO_SHA512}) {
+      net::ct::DigitallySigned original;
+      original.hash_algorithm = hash_alg;
+      original.signature_algorithm = sig_alg;
+      original.signature_data.assign(5, static_cast<char>(hash_alg));
+
+      net::ct::DigitallySigned copied;
+      EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::DigitallySigned>(
+          &original, &copied))
+          << "with hash " << hash_alg << " and sig " << sig_alg;
+      EXPECT_EQ(original.hash_algorithm, copied.hash_algorithm);
+      EXPECT_EQ(original.signature_algorithm, copied.signature_algorithm);
+      EXPECT_EQ(original.signature_data, copied.signature_data);
+    }
+  }
+}
+
+TEST(DigitallySignedTraitsTest, EmptySignatureRejected) {
+  net::ct::DigitallySigned original;
+  original.hash_algorithm = net::ct::DigitallySigned::HASH_ALGO_SHA256;
+  original.signature_algorithm = net::ct::DigitallySigned::SIG_ALGO_ECDSA;
+  original.signature_data.clear();
+
+  net::ct::DigitallySigned copied;
+  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::DigitallySigned>(
+      &original, &copied));
+}
+
+TEST(DigitallySignedTraitsTest, OutOfBoundsEnumsRejected) {
+  net::ct::DigitallySigned original;
+  original.hash_algorithm =
+      static_cast<net::ct::DigitallySigned::HashAlgorithm>(-1);
+  original.signature_algorithm = net::ct::DigitallySigned::SIG_ALGO_ECDSA;
+  original.signature_data.assign(32, '\x01');
+
+  net::ct::DigitallySigned copied;
+  EXPECT_DCHECK_DEATH(
+      mojo::test::SerializeAndDeserialize<mojom::DigitallySigned>(&original,
+                                                                  &copied));
+}
+
+}  // namespace
+}  // namespace network
diff --git a/services/network/public/cpp/signed_tree_head.typemap b/services/network/public/cpp/signed_tree_head.typemap
new file mode 100644
index 0000000..92dbaee
--- /dev/null
+++ b/services/network/public/cpp/signed_tree_head.typemap
@@ -0,0 +1,18 @@
+# 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.
+
+mojom = "//services/network/public/mojom/signed_tree_head.mojom"
+public_headers = [ "//net/cert/signed_tree_head.h" ]
+traits_headers =
+    [ "//services/network/public/cpp/signed_tree_head_mojom_traits.h" ]
+sources = [
+  "//services/network/public/cpp/signed_tree_head_mojom_traits.cc",
+]
+type_mappings = [
+  "network.mojom.SignedTreeHead=net::ct::SignedTreeHead",
+  "network.mojom.SignedTreeHeadVersion=net::ct::SignedTreeHead::Version",
+]
+public_deps = [
+  "//net",
+]
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits.cc b/services/network/public/cpp/signed_tree_head_mojom_traits.cc
new file mode 100644
index 0000000..9195950
--- /dev/null
+++ b/services/network/public/cpp/signed_tree_head_mojom_traits.cc
@@ -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.
+
+#include "services/network/public/cpp/signed_tree_head_mojom_traits.h"
+
+#include <memory>
+#include <vector>
+
+#include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "services/network/public/cpp/digitally_signed_mojom_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<
+    network::mojom::SignedTreeHeadDataView,
+    net::ct::SignedTreeHead>::Read(network::mojom::SignedTreeHeadDataView data,
+                                   net::ct::SignedTreeHead* out) {
+  std::vector<uint8_t> sha256_root_hash;
+  if (!data.ReadVersion(&out->version) ||
+      !data.ReadTimestamp(&out->timestamp) ||
+      !data.ReadSignature(&out->signature) || !data.ReadLogId(&out->log_id) ||
+      !data.ReadSha256RootHash(&sha256_root_hash)) {
+    return false;
+  }
+  if (out->log_id.empty()) {
+    return false;
+  }
+
+  out->tree_size = data.tree_size();
+
+  // The Mojo bindings should have validated the size constraint as part of
+  // ReadSha256RootHash().
+  DCHECK_EQ(sha256_root_hash.size(), sizeof(out->sha256_root_hash));
+  memcpy(out->sha256_root_hash, sha256_root_hash.data(),
+         sizeof(out->sha256_root_hash));
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits.h b/services/network/public/cpp/signed_tree_head_mojom_traits.h
new file mode 100644
index 0000000..f38cf4c7
--- /dev/null
+++ b/services/network/public/cpp/signed_tree_head_mojom_traits.h
@@ -0,0 +1,75 @@
+// 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 SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
+
+#include "base/containers/span.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "net/cert/signed_tree_head.h"
+#include "services/network/public/mojom/digitally_signed.mojom.h"
+#include "services/network/public/mojom/signed_tree_head.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<network::mojom::SignedTreeHeadVersion,
+                  net::ct::SignedTreeHead::Version> {
+  static network::mojom::SignedTreeHeadVersion ToMojom(
+      net::ct::SignedTreeHead::Version input) {
+    switch (input) {
+      case net::ct::SignedTreeHead::V1:
+        return network::mojom::SignedTreeHeadVersion::V1;
+    }
+    NOTREACHED();
+    return network::mojom::SignedTreeHeadVersion::kMaxValue;
+  }
+
+  static bool FromMojom(network::mojom::SignedTreeHeadVersion input,
+                        net::ct::SignedTreeHead::Version* output) {
+    switch (input) {
+      case network::mojom::SignedTreeHeadVersion::V1:
+        *output = net::ct::SignedTreeHead::V1;
+        return true;
+    }
+    NOTREACHED();
+    return false;
+  }
+};
+
+template <>
+struct StructTraits<network::mojom::SignedTreeHeadDataView,
+                    net::ct::SignedTreeHead> {
+  static net::ct::SignedTreeHead::Version version(
+      const net::ct::SignedTreeHead& sth) {
+    return sth.version;
+  }
+  static base::Time timestamp(const net::ct::SignedTreeHead& sth) {
+    return sth.timestamp;
+  }
+  static uint64_t tree_size(const net::ct::SignedTreeHead& sth) {
+    return sth.tree_size;
+  }
+  static base::span<const uint8_t> sha256_root_hash(
+      const net::ct::SignedTreeHead& sth) {
+    return base::as_bytes(base::make_span(sth.sha256_root_hash));
+  }
+  static const net::ct::DigitallySigned& signature(
+      const net::ct::SignedTreeHead& sth) {
+    return sth.signature;
+  }
+  static base::StringPiece log_id(const net::ct::SignedTreeHead& sth) {
+    return sth.log_id;
+  }
+
+  static bool Read(network::mojom::SignedTreeHeadDataView obj,
+                   net::ct::SignedTreeHead* out);
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_SIGNED_TREE_HEAD_MOJOM_TRAITS_H_
diff --git a/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc b/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc
new file mode 100644
index 0000000..3742b832
--- /dev/null
+++ b/services/network/public/cpp/signed_tree_head_mojom_traits_unittest.cc
@@ -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.
+
+#include "services/network/public/cpp/signed_tree_head_mojom_traits.h"
+
+#include "base/time/time.h"
+#include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/signed_tree_head.h"
+#include "net/test/ct_test_util.h"
+#include "services/network/public/cpp/digitally_signed_mojom_traits.h"
+#include "services/network/public/mojom/signed_tree_head.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+namespace {
+
+TEST(SignedTreeHeadTraitsTest, Roundtrips) {
+  net::ct::SignedTreeHead original;
+  net::ct::SignedTreeHead copied;
+
+  // First try with a populated STH.
+  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
+  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
+      &original, &copied));
+  EXPECT_EQ(original, copied);
+
+  // Then try an STH for an empty tree.
+  ASSERT_TRUE(net::ct::GetSampleEmptySignedTreeHead(&original));
+  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
+      &original, &copied));
+  EXPECT_EQ(original, copied);
+
+  // Then try a syntactically-valid but semantically-bad STH.
+  ASSERT_TRUE(net::ct::GetBadEmptySignedTreeHead(&original));
+  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
+      &original, &copied));
+  EXPECT_EQ(original, copied);
+}
+
+TEST(SignedTreeHeadTraitsTest, RequiresLogId) {
+  net::ct::SignedTreeHead original;
+  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
+  original.log_id.clear();
+
+  net::ct::SignedTreeHead copied;
+  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
+      &original, &copied));
+}
+
+TEST(SignedTreeHeadTraitsTest, RequiresValidDigitallySigned) {
+  net::ct::SignedTreeHead original;
+  ASSERT_TRUE(net::ct::GetSampleSignedTreeHead(&original));
+  original.signature.signature_data.clear();
+
+  net::ct::SignedTreeHead copied;
+  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::SignedTreeHead>(
+      &original, &copied));
+}
+
+}  // namespace
+}  // namespace network
diff --git a/services/network/public/cpp/typemaps.gni b/services/network/public/cpp/typemaps.gni
index 48f0b228..515e5e9 100644
--- a/services/network/public/cpp/typemaps.gni
+++ b/services/network/public/cpp/typemaps.gni
@@ -5,6 +5,7 @@
 typemaps = [
   "//services/network/public/cpp/cookie_manager.typemap",
   "//services/network/public/cpp/cors_error_status.typemap",
+  "//services/network/public/cpp/digitally_signed.typemap",
   "//services/network/public/cpp/http_request_headers.typemap",
   "//services/network/public/cpp/mutable_network_traffic_annotation_tag.typemap",
   "//services/network/public/cpp/mutable_partial_network_traffic_annotation_tag.typemap",
@@ -12,6 +13,7 @@
   "//services/network/public/cpp/network_types.typemap",
   "//services/network/public/cpp/proxy_config.typemap",
   "//services/network/public/cpp/proxy_config_with_annotation.typemap",
+  "//services/network/public/cpp/signed_tree_head.typemap",
   "//services/network/public/cpp/url_loader_completion_status.typemap",
   "//services/network/public/cpp/url_request.typemap",
   "//services/network/public/cpp/url_request_redirect_info.typemap",
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 3822279..534ad7b93 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -75,6 +75,8 @@
   sources = [
     "cookie_manager.mojom",
     "cors.mojom",
+    "ct_log_info.mojom",
+    "digitally_signed.mojom",
     "fetch_api.mojom",
     "http_request_headers.mojom",
     "network_change_manager.mojom",
@@ -86,7 +88,9 @@
     "proxy_config_with_annotation.mojom",
     "request_context_frame_type.mojom",
     "restricted_cookie_manager.mojom",
+    "signed_tree_head.mojom",
     "tcp_socket.mojom",
+    "tls_socket.mojom",
     "url_loader.mojom",
     "url_loader_factory.mojom",
   ]
diff --git a/services/network/public/mojom/ct_log_info.mojom b/services/network/public/mojom/ct_log_info.mojom
new file mode 100644
index 0000000..f4c26f7
--- /dev/null
+++ b/services/network/public/mojom/ct_log_info.mojom
@@ -0,0 +1,22 @@
+// 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 network.mojom;
+
+// A single Certificate Transparency Log configuration.
+struct CTLogInfo {
+  // The DER-encoded SubjectPublicKeyInfo of the log.
+  // TODO(rsleevi): Convert this to array<uint8> when net::CTLogVerifier uses
+  // base::span<>
+  string public_key;
+
+  // The human-readable, log-supplied log name. Note that this will not be
+  // translated.
+  string name;
+
+  // The DNS API endpoint for the log.
+  // This will be used as the parent domain for all queries about the log.
+  // See https://github.com/google/certificate-transparency-rfcs/blob/master/dns/draft-ct-over-dns.md
+  string dns_api_endpoint;
+};
diff --git a/services/network/public/mojom/digitally_signed.mojom b/services/network/public/mojom/digitally_signed.mojom
new file mode 100644
index 0000000..c1263443
--- /dev/null
+++ b/services/network/public/mojom/digitally_signed.mojom
@@ -0,0 +1,30 @@
+// 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 network.mojom;
+
+// Mirror of net::ct::DigitallySigned::HashAlgorithm
+enum HashAlgorithm {
+  HASH_ALGO_NONE = 0,
+  HASH_ALGO_MD5 = 1,
+  HASH_ALGO_SHA1 = 2,
+  HASH_ALGO_SHA224 = 3,
+  HASH_ALGO_SHA256 = 4,
+  HASH_ALGO_SHA384 = 5,
+  HASH_ALGO_SHA512 = 6
+};
+
+// Mirror of net::ct::DigitallySigned::SignatureAlgorithm
+enum SignatureAlgorithm {
+  SIG_ALGO_ANONYMOUS = 0,
+  SIG_ALGO_RSA = 1,
+  SIG_ALGO_DSA = 2,
+  SIG_ALGO_ECDSA = 3
+};
+
+// Mirror of net::ct::DigitallySigned
+struct DigitallySigned {
+  HashAlgorithm hash_algorithm;
+  SignatureAlgorithm signature_algorithm;
+  array<uint8> signature;
+};
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index d643a72..72b64f0 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -11,6 +11,7 @@
 import "net/interfaces/address_list.mojom";
 import "net/interfaces/ip_endpoint.mojom";
 import "services/network/public/mojom/cookie_manager.mojom";
+import "services/network/public/mojom/ct_log_info.mojom";
 import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
 import "services/network/public/mojom/network_change_manager.mojom";
 import "services/network/public/mojom/proxy_config.mojom";
@@ -19,6 +20,7 @@
 import "services/network/public/mojom/udp_socket.mojom";
 import "services/network/public/mojom/url_loader.mojom";
 import "services/network/public/mojom/network_param.mojom";
+import "services/network/public/mojom/signed_tree_head.mojom";
 import "services/network/public/mojom/url_loader_factory.mojom";
 import "services/network/public/mojom/websocket.mojom";
 import "services/proxy_resolver/public/mojom/proxy_resolver.mojom";
@@ -90,6 +92,14 @@
   // Must be false if built without FTP support.
   bool enable_ftp_url_support = false;
 
+  // True if the "Certificate Transparency in Chrome" policy (see
+  // https://github.com/chromium/ct-policy/blob/master/ct_policy.md) should
+  // be enforced for certificates and connections.
+  //
+  // See //net/docs/certificate-transparency.md before setting this flag to
+  // true.
+  bool enforce_chrome_ct_policy = false;
+
   // Enables HTTP/0.9 on ports other than 80 for HTTP and 443 for HTTPS.
   bool http_09_on_non_default_ports_enabled = false;
 
@@ -129,6 +139,11 @@
   // The GSSAPI library name.
   // Only used on (OS_POSIX && !OS_ANDROID) platform.
   string gssapi_library_name;
+
+  // The Certificate Transparency logs that are known to the client. SCTs from
+  // these logs will be extracted and verified; other SCTs will be treated as
+  // unrecognized.
+  array<CTLogInfo> ct_logs;
 };
 
 struct NetworkConditions {
@@ -348,7 +363,7 @@
       net.interfaces.AddressList remote_addr_list,
       MutableNetworkTrafficAnnotationTag traffic_annotation,
       TCPConnectedSocket& socket,
-      TCPConnectedSocketObserver? observer)
+      SocketObserver? observer)
      => (int32 result,
          handle<data_pipe_consumer>? receive_stream,
          handle<data_pipe_producer>? send_stream);
@@ -470,4 +485,9 @@
 
   // Gets the accumulated network usage since the start/restart of the service.
   GetTotalNetworkUsages() => (array<NetworkUsage> total_network_usages);
+
+  // Update Signed Tree Heads (STH) used in the handling of Certificate
+  // Transparency. Broadcast to each NetworkContext using the NetworkService.
+  // NetworkContextes ignore STHs from unrecognized logs.
+  UpdateSignedTreeHead(SignedTreeHead signed_tree_head);
 };
diff --git a/services/network/public/mojom/signed_tree_head.mojom b/services/network/public/mojom/signed_tree_head.mojom
new file mode 100644
index 0000000..6296dc6
--- /dev/null
+++ b/services/network/public/mojom/signed_tree_head.mojom
@@ -0,0 +1,27 @@
+// 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 network.mojom;
+
+import "mojo/public/mojom/base/time.mojom";
+import "services/network/public/mojom/digitally_signed.mojom";
+
+// Mirror of net::ct::SignedTreeHead::Version
+enum SignedTreeHeadVersion {
+  // The Version enum in RFC 6962, Section 3.2.
+  V1 = 0,
+};
+
+// Mirror of net::ct::SignedTreeHead.
+// These fields are defined in Section 3.5 and Section 4.3 of RFC 6962.
+struct SignedTreeHead {
+  SignedTreeHeadVersion version;
+  mojo_base.mojom.Time timestamp;
+  uint64 tree_size;
+  array<uint8, 32> sha256_root_hash;
+  DigitallySigned signature;
+
+  // Added in RFC6962-bis, Appendix A, but useful with RFC 6962 to track
+  // which log a given STH is associated with.
+  string log_id;
+};
diff --git a/services/network/public/mojom/tcp_socket.mojom b/services/network/public/mojom/tcp_socket.mojom
index 3606f47..9359e8b2 100644
--- a/services/network/public/mojom/tcp_socket.mojom
+++ b/services/network/public/mojom/tcp_socket.mojom
@@ -5,10 +5,13 @@
 module network.mojom;
 
 import "net/interfaces/ip_endpoint.mojom";
+import "services/network/public/mojom/tls_socket.mojom";
+import "services/network/public/mojom/network_param.mojom";
+import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
 
 // Represents a connected TCP socket. Writes and Reads are through the data
 // pipes supplied upon construction. Consumer should use
-// TCPConnectedSocketObserver interface to get notified about any error occurred
+// SocketObserver interface to get notified about any error occurred
 // during reading or writing to data pipes. Consumer can close the socket by
 // destroying the interface pointer.
 interface TCPConnectedSocket {
@@ -16,13 +19,28 @@
   // net::OK and |local_addr| contains the local address of the socket. On
   // failure, |local_addr| is null and |net_error| is a net error code.
   GetLocalAddress() => (int32 net_error, net.interfaces.IPEndPoint? local_addr);
+
+  // Upgrades a TCP socket to a TLS client socket.
+  // IMPORTANT: Caller needs close the previous send and receive pipes before
+  // this method can complete asynchronously.
+  //
+  // On success, |net_error| is net::OK. Caller is to use |send_stream| to send
+  // data and |receive_stream| to receive data over the connection. On failure,
+  // |result| is a network error code.
+  UpgradeToTLS(HostPortPair host_port_pair,
+               MutableNetworkTrafficAnnotationTag traffic_annotation,
+               TLSClientSocket& request,
+               SocketObserver? observer)
+      => (int32 net_error,
+          handle<data_pipe_consumer>? receive_stream,
+          handle<data_pipe_producer>? send_stream);
 };
 
-// Interface to listen for network connection error on a TCPConnectedSocket.
-// Because data pipe doesn't surface any network connection error, if a network
-// error happens during a read or a write, consumer can find out about it by
-// implementing this interface.
-interface TCPConnectedSocketObserver {
+// Interface to listen for network connection error on a TCPConnectedSocket or
+// a TLSClientSocket. Because data pipe doesn't surface any network connection
+// error, if a network error happens during a read or a write, consumer can find
+// out about it by implementing this interface.
+interface SocketObserver {
   // Called when an error occurs during reading from the network. The producer
   // side of |receive_stream| will be closed.
   OnReadError(int32 net_error);
@@ -45,7 +63,7 @@
   // |backlog| is a number that is specified when requesting TCPServerSocket. If
   // more than |backlog| number of Accept()s are outstanding,
   // net::ERR_INSUFFICIENT_RESOUCES will be returned.
-  Accept(TCPConnectedSocketObserver? observer)
+  Accept(SocketObserver? observer)
       => (int32 net_error,
           net.interfaces.IPEndPoint? remote_addr,
           TCPConnectedSocket? connected_socket,
diff --git a/services/network/public/mojom/tls_socket.mojom b/services/network/public/mojom/tls_socket.mojom
new file mode 100644
index 0000000..131edc2
--- /dev/null
+++ b/services/network/public/mojom/tls_socket.mojom
@@ -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.
+
+module network.mojom;
+
+import "net/interfaces/ip_endpoint.mojom";
+
+// Represents a connected TLS client socket. Writes and Reads are through the
+// data pipes supplied upon construction. Consumer should use SocketObserver
+// interface to get notified about any error occurred during reading or writing
+// to data pipes. Consumer can close the socket by destroying the interface
+// pointer.
+interface TLSClientSocket {
+};
diff --git a/services/network/socket_data_pump.cc b/services/network/socket_data_pump.cc
index 5157ca77..d0e70a4 100644
--- a/services/network/socket_data_pump.cc
+++ b/services/network/socket_data_pump.cc
@@ -16,20 +16,23 @@
 #include "net/log/net_log.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
+#include "services/network/tls_client_socket.h"
 
 namespace network {
 
 SocketDataPump::SocketDataPump(
-    mojom::TCPConnectedSocketObserverPtr observer,
     net::StreamSocket* socket,
+    Delegate* delegate,
     mojo::ScopedDataPipeProducerHandle receive_pipe_handle,
     mojo::ScopedDataPipeConsumerHandle send_pipe_handle,
     const net::NetworkTrafficAnnotationTag& traffic_annotation)
-    : observer_(std::move(observer)),
-      socket_(socket),
+    : socket_(socket),
+      delegate_(delegate),
       receive_stream_(std::move(receive_pipe_handle)),
       receive_stream_watcher_(FROM_HERE,
                               mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+      receive_stream_close_watcher_(FROM_HERE,
+                                    mojo::SimpleWatcher::ArmingPolicy::MANUAL),
       send_stream_(std::move(send_pipe_handle)),
       send_stream_watcher_(FROM_HERE,
                            mojo::SimpleWatcher::ArmingPolicy::MANUAL),
@@ -45,6 +48,10 @@
       MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
       base::BindRepeating(&SocketDataPump::OnReceiveStreamWritable,
                           base::Unretained(this)));
+  receive_stream_close_watcher_.Watch(
+      receive_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      base::BindRepeating(&SocketDataPump::OnReceiveStreamClosed,
+                          base::Unretained(this)));
   ReceiveMore();
   SendMore();
 }
@@ -53,11 +60,11 @@
 
 void SocketDataPump::ReceiveMore() {
   DCHECK(receive_stream_.is_valid());
-  DCHECK(!pending_receive_buffer_);
 
   uint32_t num_bytes = 0;
+  scoped_refptr<NetToMojoPendingBuffer> pending_receive_buffer;
   MojoResult result = NetToMojoPendingBuffer::BeginWrite(
-      &receive_stream_, &pending_receive_buffer_, &num_bytes);
+      &receive_stream_, &pending_receive_buffer, &num_bytes);
   if (result == MOJO_RESULT_SHOULD_WAIT) {
     receive_stream_watcher_.ArmOrNotify();
     return;
@@ -66,22 +73,36 @@
     ShutdownReceive();
     return;
   }
-  DCHECK(pending_receive_buffer_);
+  DCHECK(pending_receive_buffer);
   scoped_refptr<net::IOBuffer> buf =
-      base::MakeRefCounted<NetToMojoIOBuffer>(pending_receive_buffer_.get());
+      base::MakeRefCounted<NetToMojoIOBuffer>(pending_receive_buffer.get());
   // Use WeakPtr here because |this| doesn't outlive |socket_|.
-  int read_result =
-      socket_->Read(buf.get(), base::saturated_cast<int>(num_bytes),
-                    base::BindRepeating(&SocketDataPump::OnNetworkReadCompleted,
-                                        weak_factory_.GetWeakPtr()));
-  if (read_result == net::ERR_IO_PENDING)
+  int read_result = socket_->ReadIfReady(
+      buf.get(), base::saturated_cast<int>(num_bytes),
+      base::BindRepeating(&SocketDataPump::OnNetworkReadIfReadyCompleted,
+                          weak_factory_.GetWeakPtr()));
+  receive_stream_ =
+      pending_receive_buffer->Complete(read_result >= 0 ? read_result : 0);
+  if (read_result == net::ERR_IO_PENDING) {
+    read_if_ready_pending_ = true;
+    receive_stream_close_watcher_.ArmOrNotify();
     return;
-  OnNetworkReadCompleted(read_result);
+  }
+  // Handle EOF.
+  if (read_result == net::OK) {
+    ShutdownReceive();
+    return;
+  }
+  OnNetworkReadIfReadyCompleted(read_result);
+}
+
+void SocketDataPump::OnReceiveStreamClosed(MojoResult result) {
+  ShutdownReceive();
+  return;
 }
 
 void SocketDataPump::OnReceiveStreamWritable(MojoResult result) {
   DCHECK(receive_stream_.is_valid());
-  DCHECK(!pending_receive_buffer_);
 
   if (result != MOJO_RESULT_OK) {
     ShutdownReceive();
@@ -90,17 +111,17 @@
   ReceiveMore();
 }
 
-void SocketDataPump::OnNetworkReadCompleted(int result) {
-  DCHECK(!receive_stream_.is_valid());
-  DCHECK(pending_receive_buffer_);
+void SocketDataPump::OnNetworkReadIfReadyCompleted(int result) {
+  DCHECK(receive_stream_.is_valid());
+  if (read_if_ready_pending_) {
+    DCHECK_GE(net::OK, result);
+    read_if_ready_pending_ = false;
+  }
 
-  if (result < 0 && observer_)
-    observer_->OnReadError(result);
+  if (result < 0 && delegate_)
+    delegate_->OnNetworkReadError(result);
 
-  receive_stream_ = pending_receive_buffer_->Complete(result >= 0 ? result : 0);
-  pending_receive_buffer_ = nullptr;
-
-  if (result <= 0) {
+  if (result < 0) {
     ShutdownReceive();
     return;
   }
@@ -109,11 +130,16 @@
 
 void SocketDataPump::ShutdownReceive() {
   DCHECK(receive_stream_.is_valid());
-  DCHECK(!pending_receive_buffer_);
 
   receive_stream_watcher_.Cancel();
-  pending_receive_buffer_ = nullptr;
+  receive_stream_close_watcher_.Cancel();
   receive_stream_.reset();
+  if (read_if_ready_pending_) {
+    int result = socket_->CancelReadIfReady();
+    DCHECK_EQ(net::OK, result);
+    read_if_ready_pending_ = false;
+  }
+  MaybeNotifyDelegate();
 }
 
 void SocketDataPump::SendMore() {
@@ -161,8 +187,8 @@
   DCHECK(pending_send_buffer_);
   DCHECK(!send_stream_.is_valid());
 
-  if (result < 0 && observer_)
-    observer_->OnWriteError(result);
+  if (result < 0 && delegate_)
+    delegate_->OnNetworkWriteError(result);
 
   // Partial write is possible.
   pending_send_buffer_->CompleteRead(result >= 0 ? result : 0);
@@ -183,6 +209,13 @@
   send_stream_watcher_.Cancel();
   pending_send_buffer_ = nullptr;
   send_stream_.reset();
+  MaybeNotifyDelegate();
+}
+
+void SocketDataPump::MaybeNotifyDelegate() {
+  if (!delegate_ || send_stream_.is_valid() || receive_stream_.is_valid())
+    return;
+  delegate_->OnShutdown();
 }
 
 }  // namespace network
diff --git a/services/network/socket_data_pump.h b/services/network/socket_data_pump.h
index 6a4c550..6b36a21 100644
--- a/services/network/socket_data_pump.h
+++ b/services/network/socket_data_pump.h
@@ -31,27 +31,49 @@
 // pipe. Specifically, it (1) reads from the network socket and writes to a mojo
 // producer pipe, and (2) reads from a mojo consumer pipe and writes to the
 // network socket. On network read/write errors, it (3) also notifies the
-// mojom::TCPConnectedSocketObserverPtr appropriately.
+// mojom::SocketObserverPtr appropriately.
 class COMPONENT_EXPORT(NETWORK_SERVICE) SocketDataPump {
  public:
+  // Interface to notify a consumer that about network errors and whether both
+  // data pipes have been shut down from the client side.
+  class Delegate {
+   public:
+    Delegate() {}
+    ~Delegate() {}
+
+    // Called when SocketDataPump detects a network read error.
+    virtual void OnNetworkReadError(int net_error) = 0;
+
+    // Called when SocketDataPump detects a network write error.
+    virtual void OnNetworkWriteError(int net_error) = 0;
+
+    // Called when SocketDataPump detects both send and receive pipes have shut
+    // down.
+    virtual void OnShutdown() = 0;
+  };
+
   // Constructs a data pump that pumps data between |socket| and mojo data
   // pipe handles. Data are read from |send_pipe_handle| and sent to |socket|.
   // Data are read from |socket| and written to |receive_pipe_handle|.
   // |traffic_annotation| is attached to all writes to |socket|. Note that
   // |socket| must outlive |this|.
-  SocketDataPump(mojom::TCPConnectedSocketObserverPtr observer,
-                 net::StreamSocket* socket,
+  SocketDataPump(net::StreamSocket* socket,
+                 Delegate* delegate,
                  mojo::ScopedDataPipeProducerHandle receive_pipe_handle,
                  mojo::ScopedDataPipeConsumerHandle send_pipe_handle,
                  const net::NetworkTrafficAnnotationTag& traffic_annotation);
   ~SocketDataPump();
 
  private:
+  // Maybe notifies |delegate_| of the shutdown if both pipes are closed.
+  void MaybeNotifyDelegate();
+
   // "Receiving" in this context means reading from |socket_| and writing to
   // the Mojo |receive_stream_|.
   void ReceiveMore();
   void OnReceiveStreamWritable(MojoResult result);
-  void OnNetworkReadCompleted(int result);
+  void OnReceiveStreamClosed(MojoResult result);
+  void OnNetworkReadIfReadyCompleted(int result);
   void ShutdownReceive();
 
   // "Writing" is reading from the Mojo |send_stream_| and writing to the
@@ -61,9 +83,8 @@
   void OnNetworkWriteCompleted(int result);
   void ShutdownSend();
 
-  mojom::TCPConnectedSocketObserverPtr observer_;
-
-  net::StreamSocket* socket_;
+  net::StreamSocket* const socket_;
+  Delegate* const delegate_;
 
   // The *stream handles will be null when there's a pending read from |socket_|
   // to |pending_receive_buffer_|, or while there is a pending write from
@@ -75,8 +96,13 @@
 
   // For reading from the network and writing to Mojo pipe.
   mojo::ScopedDataPipeProducerHandle receive_stream_;
-  scoped_refptr<NetToMojoPendingBuffer> pending_receive_buffer_;
   mojo::SimpleWatcher receive_stream_watcher_;
+  // A separate watcher is needed to observe |receive_stream_|'s close event, so
+  // that when the client shuts down their end of the pipe for
+  // TCPConnectedSocket::UpgradeToTLS() during the waiting of the async callback
+  // of ReadIfReady, |this| can be notified of the shutdown.
+  mojo::SimpleWatcher receive_stream_close_watcher_;
+  bool read_if_ready_pending_ = false;
 
   // For reading from the Mojo pipe and writing to the network.
   mojo::ScopedDataPipeConsumerHandle send_stream_;
diff --git a/services/network/socket_data_pump_unittest.cc b/services/network/socket_data_pump_unittest.cc
index b2f28b1..208449e 100644
--- a/services/network/socket_data_pump_unittest.cc
+++ b/services/network/socket_data_pump_unittest.cc
@@ -19,6 +19,7 @@
 #include "net/socket/socket_test_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/mojo_socket_test_util.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/udp_socket.mojom.h"
 #include "services/network/socket_factory.h"
@@ -28,25 +29,13 @@
 
 namespace network {
 
-namespace {
-
-class TestSocketObserver : public mojom::TCPConnectedSocketObserver {
+// Test delegate to wait on network read/write errors.
+class TestSocketDataPumpDelegate : public SocketDataPump::Delegate {
  public:
-  TestSocketObserver() : binding_(this) {}
-  ~TestSocketObserver() override {
-    EXPECT_EQ(net::OK, read_error_);
-    EXPECT_EQ(net::OK, write_error_);
-  }
+  TestSocketDataPumpDelegate() {}
+  ~TestSocketDataPumpDelegate() {}
 
-  // Returns a mojo pointer. This can only be called once.
-  mojom::TCPConnectedSocketObserverPtr GetObserverPtr() {
-    DCHECK(!binding_);
-    mojom::TCPConnectedSocketObserverPtr ptr;
-    binding_.Bind(mojo::MakeRequest(&ptr));
-    return ptr;
-  }
-
-  // Waits for Read and Write error. Returns the error observed.
+  // Waits for read error. Returns the error observed.
   int WaitForReadError() {
     read_loop_.Run();
     int error = read_error_;
@@ -54,6 +43,7 @@
     return error;
   }
 
+  // Waits for write error. Returns the error observed.
   int WaitForWriteError() {
     write_loop_.Run();
     int error = write_error_;
@@ -61,29 +51,29 @@
     return error;
   }
 
+  // Waits for shutdown.
+  void WaitForShutdown() { shutdown_loop_.Run(); }
+
  private:
-  // mojom::TCPConnectedSocketObserver implementation.
-  void OnReadError(int net_error) override {
-    read_error_ = net_error;
+  void OnNetworkReadError(int error) override {
+    read_error_ = error;
     read_loop_.Quit();
   }
-
-  void OnWriteError(int net_error) override {
-    write_error_ = net_error;
+  void OnNetworkWriteError(int error) override {
+    write_error_ = error;
     write_loop_.Quit();
   }
+  void OnShutdown() override { shutdown_loop_.Quit(); }
 
   int read_error_ = net::OK;
   int write_error_ = net::OK;
   base::RunLoop read_loop_;
   base::RunLoop write_loop_;
-  mojo::Binding<mojom::TCPConnectedSocketObserver> binding_;
+  base::RunLoop shutdown_loop_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestSocketObserver);
+  DISALLOW_COPY_AND_ASSIGN(TestSocketDataPumpDelegate);
 };
 
-}  // namespace
-
 class SocketDataPumpTest : public testing::Test,
                            public ::testing::WithParamInterface<net::IoMode> {
  public:
@@ -96,6 +86,7 @@
   // to populate the read/write data of the mock socket.
   void Init(net::StaticSocketDataProvider* data_provider) {
     mock_client_socket_factory_.AddSocketDataProvider(data_provider);
+    mock_client_socket_factory_.set_enable_read_if_ready(true);
     mojo::DataPipe send_pipe;
     mojo::DataPipe receive_pipe;
     receive_handle_ = std::move(receive_pipe.consumer_handle);
@@ -109,8 +100,7 @@
       result = callback.WaitForResult();
     EXPECT_EQ(net::OK, result);
     data_pump_ = std::make_unique<SocketDataPump>(
-        test_observer_.GetObserverPtr(), socket_.get(),
-        std::move(receive_pipe.producer_handle),
+        socket_.get(), delegate(), std::move(receive_pipe.producer_handle),
         std::move(send_pipe.consumer_handle), TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
@@ -134,7 +124,7 @@
     return received_contents;
   }
 
-  TestSocketObserver* observer() { return &test_observer_; }
+  TestSocketDataPumpDelegate* delegate() { return &test_delegate_; }
 
   mojo::ScopedDataPipeConsumerHandle receive_handle_;
   mojo::ScopedDataPipeProducerHandle send_handle_;
@@ -142,7 +132,7 @@
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   net::MockClientSocketFactory mock_client_socket_factory_;
-  TestSocketObserver test_observer_;
+  TestSocketDataPumpDelegate test_delegate_;
   std::unique_ptr<net::StreamSocket> socket_;
   std::unique_ptr<SocketDataPump> data_pump_;
 
@@ -253,7 +243,7 @@
   net::StaticSocketDataProvider data_provider(reads, writes);
   Init(&data_provider);
   EXPECT_EQ("", Read(&receive_handle_, 1));
-  EXPECT_EQ(net::ERR_FAILED, observer()->WaitForReadError());
+  EXPECT_EQ(net::ERR_FAILED, delegate()->WaitForReadError());
   // Writes can proceed even though there is a read error.
   uint32_t num_bytes = strlen(kTestMsg);
   EXPECT_EQ(MOJO_RESULT_OK, send_handle_->WriteData(&kTestMsg, &num_bytes,
@@ -277,7 +267,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, send_handle_->WriteData(&kTestMsg, &num_bytes,
                                                     MOJO_WRITE_DATA_FLAG_NONE));
   EXPECT_EQ(strlen(kTestMsg), num_bytes);
-  EXPECT_EQ(net::ERR_FAILED, observer()->WaitForWriteError());
+  EXPECT_EQ(net::ERR_FAILED, delegate()->WaitForWriteError());
   // Reads can proceed even though there is a read error.
   EXPECT_EQ(kTestMsg, Read(&receive_handle_, strlen(kTestMsg)));
 
@@ -286,4 +276,15 @@
   EXPECT_TRUE(data_provider.AllWriteDataConsumed());
 }
 
+TEST_P(SocketDataPumpTest, PipesShutdown) {
+  net::IoMode mode = GetParam();
+  net::MockRead reads[] = {net::MockRead(mode, net::OK)};
+  net::StaticSocketDataProvider data_provider(reads,
+                                              base::span<net::MockWrite>());
+  Init(&data_provider);
+  send_handle_.reset();
+  receive_handle_.reset();
+  delegate()->WaitForShutdown();
+}
+
 }  // namespace network
diff --git a/services/network/socket_factory.cc b/services/network/socket_factory.cc
index bd6cbbc4..7e667b6 100644
--- a/services/network/socket_factory.cc
+++ b/services/network/socket_factory.cc
@@ -4,28 +4,44 @@
 
 #include "services/network/socket_factory.h"
 
+#include <string>
 #include <utility>
 
 #include "base/optional.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_errors.h"
 #include "net/log/net_log.h"
 #include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/ssl/ssl_config.h"
+#include "net/ssl/ssl_config_service.h"
 #include "net/url_request/url_request_context.h"
 #include "services/network/tcp_connected_socket.h"
+#include "services/network/tls_client_socket.h"
+
 #include "services/network/udp_socket.h"
 
 namespace network {
 
 SocketFactory::SocketFactory(net::NetLog* net_log,
                              net::URLRequestContext* url_request_context)
-    : net_log_(net_log) {
+    : net_log_(net_log),
+      ssl_client_socket_context_(
+          url_request_context->cert_verifier(),
+          nullptr, /* TODO(rkn): ChannelIDService is not thread safe. */
+          url_request_context->transport_security_state(),
+          url_request_context->cert_transparency_verifier(),
+          url_request_context->ct_policy_enforcer(),
+          std::string() /* TODO(rsleevi): Ensure a proper unique shard. */),
+      client_socket_factory_(nullptr),
+      ssl_config_service_(url_request_context->ssl_config_service()) {
   if (url_request_context->GetNetworkSessionContext()) {
     client_socket_factory_ =
         url_request_context->GetNetworkSessionContext()->client_socket_factory;
-  } else {
-    client_socket_factory_ = nullptr;
   }
+  if (!client_socket_factory_)
+    client_socket_factory_ = net::ClientSocketFactory::GetDefaultFactory();
 }
 
 SocketFactory::~SocketFactory() {}
@@ -60,10 +76,10 @@
     const net::AddressList& remote_addr_list,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     mojom::TCPConnectedSocketRequest request,
-    mojom::TCPConnectedSocketObserverPtr observer,
+    mojom::SocketObserverPtr observer,
     mojom::NetworkContext::CreateTCPConnectedSocketCallback callback) {
   auto socket = std::make_unique<TCPConnectedSocket>(
-      std::move(observer), net_log_, client_socket_factory_,
+      std::move(observer), net_log_, this, client_socket_factory_,
       traffic_annotation);
   TCPConnectedSocket* socket_raw = socket.get();
   tcp_connected_socket_bindings_.AddBinding(std::move(socket),
@@ -71,6 +87,25 @@
   socket_raw->Connect(local_addr, remote_addr_list, std::move(callback));
 }
 
+void SocketFactory::CreateTLSClientSocket(
+    const net::HostPortPair& host_port_pair,
+    mojom::TLSClientSocketRequest request,
+    std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+    mojom::SocketObserverPtr observer,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+    mojom::TCPConnectedSocket::UpgradeToTLSCallback callback) {
+  auto socket = std::make_unique<TLSClientSocket>(
+      std::move(request), std::move(observer),
+      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation));
+  TLSClientSocket* socket_raw = socket.get();
+  tls_socket_bindings_.AddBinding(std::move(socket), std::move(request));
+  net::SSLConfig ssl_config;
+  ssl_config_service_->GetSSLConfig(&ssl_config);
+  socket_raw->Connect(host_port_pair, ssl_config, std::move(tcp_socket),
+                      ssl_client_socket_context_, client_socket_factory_,
+                      std::move(callback));
+}
+
 void SocketFactory::OnAccept(std::unique_ptr<TCPConnectedSocket> socket,
                              mojom::TCPConnectedSocketRequest request) {
   tcp_connected_socket_bindings_.AddBinding(std::move(socket),
diff --git a/services/network/socket_factory.h b/services/network/socket_factory.h
index d1e6bc86..6d23f4af 100644
--- a/services/network/socket_factory.h
+++ b/services/network/socket_factory.h
@@ -11,27 +11,30 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/strong_binding_set.h"
+#include "net/socket/ssl_client_socket.h"
 #include "net/socket/tcp_socket.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/tcp_socket.mojom.h"
+#include "services/network/public/mojom/tls_socket.mojom.h"
 #include "services/network/public/mojom/udp_socket.mojom.h"
+#include "services/network/tcp_connected_socket.h"
 #include "services/network/tcp_server_socket.h"
 
 namespace net {
+class ClientSocketHandle;
 class ClientSocketFactory;
 class NetLog;
+class SSLConfigService;
 }  // namespace net
 
 namespace network {
 
-class UDPSocket;
-class TCPConnectedSocket;
-
-// Helper class that handles UDPSocketRequest. It takes care of destroying the
-// UDPSocket implementation instances when mojo pipes are broken.
+// Helper class that handles socket requests. It takes care of destroying
+// socket implementation instances when mojo  pipes are broken.
 class COMPONENT_EXPORT(NETWORK_SERVICE) SocketFactory
-    : public TCPServerSocket::Delegate {
+    : public TCPServerSocket::Delegate,
+      public TCPConnectedSocket::Delegate {
  public:
   // Constructs a SocketFactory. If |net_log| is non-null, it is used to
   // log NetLog events when logging is enabled. |net_log| used to must outlive
@@ -53,7 +56,7 @@
       const net::AddressList& remote_addr_list,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojom::TCPConnectedSocketRequest request,
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       mojom::NetworkContext::CreateTCPConnectedSocketCallback callback);
 
  private:
@@ -61,12 +64,24 @@
   void OnAccept(std::unique_ptr<TCPConnectedSocket> socket,
                 mojom::TCPConnectedSocketRequest request) override;
 
-  net::NetLog* net_log_;
+  // TCPConnectedSocket::Delegate implementation:
+  void CreateTLSClientSocket(
+      const net::HostPortPair& host_port_pair,
+      mojom::TLSClientSocketRequest request,
+      std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+      mojom::SocketObserverPtr observer,
+      const net::NetworkTrafficAnnotationTag& traffic_annotation,
+      mojom::TCPConnectedSocket::UpgradeToTLSCallback callback) override;
+
+  net::NetLog* const net_log_;
+  const net::SSLClientSocketContext ssl_client_socket_context_;
   net::ClientSocketFactory* client_socket_factory_;
+  scoped_refptr<net::SSLConfigService> ssl_config_service_;
   mojo::StrongBindingSet<mojom::UDPSocket> udp_socket_bindings_;
   mojo::StrongBindingSet<mojom::TCPServerSocket> tcp_server_socket_bindings_;
   mojo::StrongBindingSet<mojom::TCPConnectedSocket>
       tcp_connected_socket_bindings_;
+  mojo::StrongBindingSet<mojom::TLSClientSocket> tls_socket_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(SocketFactory);
 };
diff --git a/services/network/tcp_connected_socket.cc b/services/network/tcp_connected_socket.cc
index 23b9ce78..ee9d57b0 100644
--- a/services/network/tcp_connected_socket.cc
+++ b/services/network/tcp_connected_socket.cc
@@ -13,35 +13,40 @@
 #include "net/base/net_errors.h"
 #include "net/log/net_log.h"
 #include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "services/network/tls_client_socket.h"
 
 namespace network {
 
 TCPConnectedSocket::TCPConnectedSocket(
-    mojom::TCPConnectedSocketObserverPtr observer,
+    mojom::SocketObserverPtr observer,
     net::NetLog* net_log,
+    Delegate* delegate,
     net::ClientSocketFactory* client_socket_factory,
     const net::NetworkTrafficAnnotationTag& traffic_annotation)
     : observer_(std::move(observer)),
       net_log_(net_log),
-      client_socket_factory_(client_socket_factory == nullptr
-                                 ? net::ClientSocketFactory::GetDefaultFactory()
-                                 : client_socket_factory),
+      delegate_(delegate),
+      client_socket_factory_(client_socket_factory),
       traffic_annotation_(traffic_annotation) {}
 
 TCPConnectedSocket::TCPConnectedSocket(
-    mojom::TCPConnectedSocketObserverPtr observer,
+    mojom::SocketObserverPtr observer,
     std::unique_ptr<net::StreamSocket> socket,
     mojo::ScopedDataPipeProducerHandle receive_pipe_handle,
     mojo::ScopedDataPipeConsumerHandle send_pipe_handle,
     const net::NetworkTrafficAnnotationTag& traffic_annotation)
-    : net_log_(nullptr),
+    : observer_(std::move(observer)),
+      net_log_(nullptr),
+      delegate_(nullptr),
       client_socket_factory_(nullptr),
       socket_(std::move(socket)),
       traffic_annotation_(traffic_annotation) {
   socket_data_pump_ = std::make_unique<SocketDataPump>(
-      std::move(observer), socket_.get(), std::move(receive_pipe_handle),
+      socket_.get(), this /*delegate*/, std::move(receive_pipe_handle),
       std::move(send_pipe_handle), traffic_annotation);
 }
+
 TCPConnectedSocket::~TCPConnectedSocket() {}
 
 void TCPConnectedSocket::Connect(
@@ -80,6 +85,35 @@
   std::move(callback).Run(result, local_addr);
 }
 
+void TCPConnectedSocket::UpgradeToTLS(
+    const net::HostPortPair& host_port_pair,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojom::TLSClientSocketRequest request,
+    mojom::SocketObserverPtr observer,
+    UpgradeToTLSCallback callback) {
+  // Wait for data pipes to be closed by the client before doing the upgrade.
+  if (socket_data_pump_) {
+    pending_upgrade_to_tls_callback_ = base::BindOnce(
+        &TCPConnectedSocket::UpgradeToTLS, base::Unretained(this),
+        host_port_pair, traffic_annotation, std::move(request),
+        std::move(observer), std::move(callback));
+    return;
+  }
+  if (!socket_ || !socket_->IsConnected()) {
+    std::move(callback).Run(net::ERR_SOCKET_NOT_CONNECTED,
+                            mojo::ScopedDataPipeConsumerHandle(),
+                            mojo::ScopedDataPipeProducerHandle());
+    return;
+  }
+  auto socket_handle = std::make_unique<net::ClientSocketHandle>();
+  socket_handle->SetSocket(std::move(socket_));
+  delegate_->CreateTLSClientSocket(
+      host_port_pair, std::move(request), std::move(socket_handle),
+      std::move(observer),
+      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
+      std::move(callback));
+}
+
 void TCPConnectedSocket::OnConnectCompleted(int result) {
   DCHECK(!connect_callback_.is_null());
   DCHECK(!socket_data_pump_);
@@ -92,14 +126,28 @@
   }
   mojo::DataPipe send_pipe;
   mojo::DataPipe receive_pipe;
-
   socket_data_pump_ = std::make_unique<SocketDataPump>(
-      std::move(observer_), socket_.get(),
-      std::move(receive_pipe.producer_handle),
+      socket_.get(), this /*delegate*/, std::move(receive_pipe.producer_handle),
       std::move(send_pipe.consumer_handle), traffic_annotation_);
   std::move(connect_callback_)
       .Run(net::OK, std::move(receive_pipe.consumer_handle),
            std::move(send_pipe.producer_handle));
 }
 
+void TCPConnectedSocket::OnNetworkReadError(int net_error) {
+  if (observer_)
+    observer_->OnReadError(net_error);
+}
+
+void TCPConnectedSocket::OnNetworkWriteError(int net_error) {
+  if (observer_)
+    observer_->OnWriteError(net_error);
+}
+
+void TCPConnectedSocket::OnShutdown() {
+  socket_data_pump_ = nullptr;
+  if (!pending_upgrade_to_tls_callback_.is_null())
+    std::move(pending_upgrade_to_tls_callback_).Run();
+}
+
 }  // namespace network
diff --git a/services/network/tcp_connected_socket.h b/services/network/tcp_connected_socket.h
index e7057a89..f20c212 100644
--- a/services/network/tcp_connected_socket.h
+++ b/services/network/tcp_connected_socket.h
@@ -25,24 +25,38 @@
 #include "services/network/socket_data_pump.h"
 
 namespace net {
-class ClientSocketFactory;
 class NetLog;
 class StreamSocket;
 class ClientSocketFactory;
+class ClientSocketHandle;
 }  // namespace net
 
 namespace network {
 
 class COMPONENT_EXPORT(NETWORK_SERVICE) TCPConnectedSocket
-    : public mojom::TCPConnectedSocket {
+    : public mojom::TCPConnectedSocket,
+      public SocketDataPump::Delegate {
  public:
+  // Interface to handle a mojom::TLSClientSocketRequest.
+  class Delegate {
+   public:
+    // Handles a mojom::TLSClientSocketRequest.
+    virtual void CreateTLSClientSocket(
+        const net::HostPortPair& host_port_pair,
+        mojom::TLSClientSocketRequest request,
+        std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+        mojom::SocketObserverPtr observer,
+        const net::NetworkTrafficAnnotationTag& traffic_annotation,
+        mojom::TCPConnectedSocket::UpgradeToTLSCallback callback) = 0;
+  };
   TCPConnectedSocket(
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       net::NetLog* net_log,
+      Delegate* delegate,
       net::ClientSocketFactory* client_socket_factory,
       const net::NetworkTrafficAnnotationTag& traffic_annotation);
   TCPConnectedSocket(
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       std::unique_ptr<net::StreamSocket> socket,
       mojo::ScopedDataPipeProducerHandle receive_pipe_handle,
       mojo::ScopedDataPipeConsumerHandle send_pipe_handle,
@@ -56,22 +70,37 @@
   // mojom::TCPConnectedSocket implementation.
   void GetLocalAddress(GetLocalAddressCallback callback) override;
 
+  void UpgradeToTLS(
+      const net::HostPortPair& host_port_pair,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojom::TLSClientSocketRequest request,
+      mojom::SocketObserverPtr observer,
+      UpgradeToTLSCallback callback) override;
+
  private:
   // Invoked when net::TCPClientSocket::Connect() completes.
   void OnConnectCompleted(int net_result);
 
-  mojom::TCPConnectedSocketObserverPtr observer_;
+  // SocketDataPump::Delegate implementation.
+  void OnNetworkReadError(int net_error) override;
+  void OnNetworkWriteError(int net_error) override;
+  void OnShutdown() override;
 
-  net::NetLog* net_log_;
-  net::ClientSocketFactory* client_socket_factory_;
+  const mojom::SocketObserverPtr observer_;
+
+  net::NetLog* const net_log_;
+  Delegate* const delegate_;
+  net::ClientSocketFactory* const client_socket_factory_;
 
   std::unique_ptr<net::StreamSocket> socket_;
 
   mojom::NetworkContext::CreateTCPConnectedSocketCallback connect_callback_;
 
+  base::OnceClosure pending_upgrade_to_tls_callback_;
+
   std::unique_ptr<SocketDataPump> socket_data_pump_;
 
-  net::NetworkTrafficAnnotationTag traffic_annotation_;
+  const net::NetworkTrafficAnnotationTag traffic_annotation_;
 
   DISALLOW_COPY_AND_ASSIGN(TCPConnectedSocket);
 };
diff --git a/services/network/tcp_server_socket.cc b/services/network/tcp_server_socket.cc
index a326f18..9c8dfb3 100644
--- a/services/network/tcp_server_socket.cc
+++ b/services/network/tcp_server_socket.cc
@@ -45,7 +45,7 @@
   return net_error;
 }
 
-void TCPServerSocket::Accept(mojom::TCPConnectedSocketObserverPtr observer,
+void TCPServerSocket::Accept(mojom::SocketObserverPtr observer,
                              AcceptCallback callback) {
   if (pending_accepts_queue_.size() >= static_cast<size_t>(backlog_)) {
     std::move(callback).Run(net::ERR_INSUFFICIENT_RESOURCES, base::nullopt,
@@ -77,9 +77,8 @@
   socket_ = std::move(socket);
 }
 
-TCPServerSocket::PendingAccept::PendingAccept(
-    AcceptCallback callback,
-    mojom::TCPConnectedSocketObserverPtr observer)
+TCPServerSocket::PendingAccept::PendingAccept(AcceptCallback callback,
+                                              mojom::SocketObserverPtr observer)
     : callback(std::move(callback)), observer(std::move(observer)) {}
 
 TCPServerSocket::PendingAccept::~PendingAccept() {}
diff --git a/services/network/tcp_server_socket.h b/services/network/tcp_server_socket.h
index 0a46c10..e15223e9 100644
--- a/services/network/tcp_server_socket.h
+++ b/services/network/tcp_server_socket.h
@@ -54,7 +54,7 @@
              net::IPEndPoint* local_addr_out);
 
   // TCPServerSocket implementation.
-  void Accept(mojom::TCPConnectedSocketObserverPtr observer,
+  void Accept(mojom::SocketObserverPtr observer,
               AcceptCallback callback) override;
   void GetLocalAddress(GetLocalAddressCallback callback) override;
 
@@ -63,19 +63,18 @@
 
  private:
   struct PendingAccept {
-    PendingAccept(AcceptCallback callback,
-                  mojom::TCPConnectedSocketObserverPtr observer);
+    PendingAccept(AcceptCallback callback, mojom::SocketObserverPtr observer);
     ~PendingAccept();
 
     AcceptCallback callback;
-    mojom::TCPConnectedSocketObserverPtr observer;
+    mojom::SocketObserverPtr observer;
   };
   // Invoked when socket_->Accept() completes.
   void OnAcceptCompleted(int result);
   // Process the next Accept() from |pending_accepts_queue_|.
   void ProcessNextAccept();
 
-  Delegate* delegate_;
+  Delegate* const delegate_;
   std::unique_ptr<net::ServerSocket> socket_;
   int backlog_;
   std::vector<std::unique_ptr<PendingAccept>> pending_accepts_queue_;
diff --git a/services/network/tcp_socket_unittest.cc b/services/network/tcp_socket_unittest.cc
index e9e130d..f09b4561 100644
--- a/services/network/tcp_socket_unittest.cc
+++ b/services/network/tcp_socket_unittest.cc
@@ -24,6 +24,7 @@
 #include "net/socket/socket_test_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/mojo_socket_test_util.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/udp_socket.mojom.h"
 #include "services/network/socket_factory.h"
@@ -99,6 +100,7 @@
     auto mock_socket = std::make_unique<net::MockTCPClientSocket>(
         net::AddressList(), nullptr /*netlog*/,
         data_providers_[next_data_provider_index_++].get());
+    mock_socket->set_enable_read_if_ready(true);
     EXPECT_EQ(net::OK, mock_socket->Connect(base::DoNothing()));
     return std::move(mock_socket);
   }
@@ -112,59 +114,6 @@
   size_t next_data_provider_index_ = 0;
 };
 
-class TestTCPConnectedSocketObserver
-    : public mojom::TCPConnectedSocketObserver {
- public:
-  TestTCPConnectedSocketObserver() : binding_(this) {}
-  ~TestTCPConnectedSocketObserver() override {
-    EXPECT_EQ(net::OK, read_error_);
-    EXPECT_EQ(net::OK, write_error_);
-  }
-
-  // Returns a mojo pointer. This can only be called once.
-  mojom::TCPConnectedSocketObserverPtr GetObserverPtr() {
-    DCHECK(!binding_);
-    mojom::TCPConnectedSocketObserverPtr ptr;
-    binding_.Bind(mojo::MakeRequest(&ptr));
-    return ptr;
-  }
-
-  // Waits for Read and Write error. Returns the error observed.
-  int WaitForReadError() {
-    read_loop_.Run();
-    int error = read_error_;
-    read_error_ = net::OK;
-    return error;
-  }
-
-  int WaitForWriteError() {
-    write_loop_.Run();
-    int error = write_error_;
-    write_error_ = net::OK;
-    return error;
-  }
-
- private:
-  // mojom::TCPConnectedSocketObserver implementation.
-  void OnReadError(int net_error) override {
-    read_error_ = net_error;
-    read_loop_.Quit();
-  }
-
-  void OnWriteError(int net_error) override {
-    write_error_ = net_error;
-    write_loop_.Quit();
-  }
-
-  int read_error_ = net::OK;
-  int write_error_ = net::OK;
-  base::RunLoop read_loop_;
-  base::RunLoop write_loop_;
-  mojo::Binding<mojom::TCPConnectedSocketObserver> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestTCPConnectedSocketObserver);
-};
-
 // A server implemented using mojom::TCPServerSocket. It owns the server socket
 // pointer and as well as client connections. SendData() and StartReading()
 // operate on the newest client connection.
@@ -343,7 +292,7 @@
 
   int CreateTCPConnectedSocketSync(
       mojom::TCPConnectedSocketRequest request,
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       const base::Optional<net::IPEndPoint>& local_addr,
       const net::IPEndPoint& remote_addr,
       mojo::ScopedDataPipeConsumerHandle* receive_pipe_handle_out,
@@ -371,13 +320,13 @@
     return net_error;
   }
 
-  TestTCPConnectedSocketObserver* observer() { return &test_observer_; }
+  TestSocketObserver* observer() { return &test_observer_; }
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   net::TestURLRequestContext url_request_context_;
   std::unique_ptr<SocketFactory> factory_;
-  TestTCPConnectedSocketObserver test_observer_;
+  TestSocketObserver test_observer_;
   mojo::StrongBindingSet<mojom::TCPServerSocket> tcp_server_socket_bindings_;
   mojo::StrongBindingSet<mojom::TCPConnectedSocket>
       tcp_connected_socket_bindings_;
@@ -648,7 +597,10 @@
     : public TCPSocketTest,
       public ::testing::WithParamInterface<net::IoMode> {
  public:
-  void SetUp() override { Init(&mock_client_socket_factory_); }
+  void SetUp() override {
+    mock_client_socket_factory_.set_enable_read_if_ready(true);
+    Init(&mock_client_socket_factory_);
+  }
 
   net::MockClientSocketFactory mock_client_socket_factory_;
 };
@@ -729,7 +681,7 @@
 }
 
 // Tests that TCPServerSocket::Accept() is used with a non-null
-// TCPConnectedSocketObserver and that the observer is invoked when a read error
+// SocketObserver and that the observer is invoked when a read error
 // occurs.
 TEST_P(TCPSocketWithMockSocketTest, ServerAcceptWithObserverReadError) {
   net::IoMode mode = GetParam();
@@ -781,9 +733,8 @@
   EXPECT_EQ(net::ERR_TIMED_OUT, observer()->WaitForReadError());
 }
 
-// Tests that TCPServerSocket::Accept() is used with a non-null
-// TCPConnectedSocketObserver  and that the observer is invoked when a write
-// error occurs.
+// Tests that TCPServerSocket::Accept() is used with a non-null SocketObserver
+// and that the observer is invoked when a write error occurs.
 TEST_P(TCPSocketWithMockSocketTest, ServerAcceptWithObserverWriteError) {
   net::IoMode mode = GetParam();
   const net::MockRead kReads[] = {net::MockRead(net::SYNCHRONOUS, net::OK)};
@@ -990,10 +941,14 @@
   mojom::TCPConnectedSocketPtr client_socket;
   net::IoMode mode = GetParam();
   const char kTestMsg[] = "hello!";
-  net::MockRead reads[] = {net::MockRead(mode, kTestMsg, strlen(kTestMsg), 0),
-                           net::MockRead(mode, net::OK)};
-  net::MockWrite writes[] = {net::MockWrite(mode, net::ERR_FAILED)};
-  net::StaticSocketDataProvider data_provider(reads, writes);
+  // The first MockRead needs to complete asynchronously because otherwise it
+  // can't be paused to happen after the MockWrite.
+  net::MockRead reads[] = {
+      net::MockRead(net::ASYNC, kTestMsg, strlen(kTestMsg), 1),
+      net::MockRead(mode, net::OK, 2)};
+  net::MockWrite writes[] = {net::MockWrite(mode, net::ERR_FAILED, 0)};
+  net::SequencedSocketData data_provider(reads, writes);
+
   net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
   data_provider.set_connect_data(
       net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 6ffebe6..a55b4489 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -80,7 +80,7 @@
       const net::AddressList& remote_addr_list,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       mojom::TCPConnectedSocketRequest socket,
-      mojom::TCPConnectedSocketObserverPtr observer,
+      mojom::SocketObserverPtr observer,
       CreateTCPConnectedSocketCallback callback) override {}
   void CreateWebSocket(mojom::WebSocketRequest request,
                        int32_t process_id,
diff --git a/services/network/tls_client_socket.cc b/services/network/tls_client_socket.cc
new file mode 100644
index 0000000..54b7815
--- /dev/null
+++ b/services/network/tls_client_socket.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/tls_client_socket.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "net/base/net_errors.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/ssl/ssl_config.h"
+#include "net/ssl/ssl_config_service.h"
+
+namespace network {
+
+TLSClientSocket::TLSClientSocket(
+    mojom::TLSClientSocketRequest request,
+    mojom::SocketObserverPtr observer,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation)
+    : observer_(std::move(observer)), traffic_annotation_(traffic_annotation) {}
+
+TLSClientSocket::~TLSClientSocket() {}
+
+void TLSClientSocket::Connect(
+    const net::HostPortPair& host_port_pair,
+    const net::SSLConfig& ssl_config,
+    std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+    const net::SSLClientSocketContext& ssl_client_socket_context,
+    net::ClientSocketFactory* socket_factory,
+    mojom::TCPConnectedSocket::UpgradeToTLSCallback callback) {
+  connect_callback_ = std::move(callback);
+  socket_ = socket_factory->CreateSSLClientSocket(std::move(tcp_socket),
+                                                  host_port_pair, ssl_config,
+                                                  ssl_client_socket_context);
+  int result = socket_->Connect(base::BindRepeating(
+      &TLSClientSocket::OnTLSConnectCompleted, base::Unretained(this)));
+  if (result != net::ERR_IO_PENDING)
+    OnTLSConnectCompleted(result);
+}
+
+void TLSClientSocket::OnTLSConnectCompleted(int result) {
+  DCHECK(!connect_callback_.is_null());
+
+  if (result != net::OK) {
+    socket_ = nullptr;
+    std::move(connect_callback_)
+        .Run(result, mojo::ScopedDataPipeConsumerHandle(),
+             mojo::ScopedDataPipeProducerHandle());
+    return;
+  }
+  mojo::DataPipe send_pipe;
+  mojo::DataPipe receive_pipe;
+  socket_data_pump_ = std::make_unique<SocketDataPump>(
+      socket_.get(), this /*delegate*/, std::move(receive_pipe.producer_handle),
+      std::move(send_pipe.consumer_handle), traffic_annotation_);
+  std::move(connect_callback_)
+      .Run(net::OK, std::move(receive_pipe.consumer_handle),
+           std::move(send_pipe.producer_handle));
+}
+
+void TLSClientSocket::OnNetworkReadError(int net_error) {
+  if (observer_)
+    observer_->OnReadError(net_error);
+}
+
+void TLSClientSocket::OnNetworkWriteError(int net_error) {
+  if (observer_)
+    observer_->OnWriteError(net_error);
+}
+
+void TLSClientSocket::OnShutdown() {
+  // Do nothing.
+}
+
+}  // namespace network
diff --git a/services/network/tls_client_socket.h b/services/network/tls_client_socket.h
new file mode 100644
index 0000000..b5ee7779
--- /dev/null
+++ b/services/network/tls_client_socket.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 SERVICES_NETWORK_TLS_CLIENT_SOCKET_H_
+#define SERVICES_NETWORK_TLS_CLIENT_SOCKET_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "net/base/address_family.h"
+#include "net/interfaces/address_family.mojom.h"
+#include "net/interfaces/ip_endpoint.mojom.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/mojom/tcp_socket.mojom.h"
+#include "services/network/public/mojom/tls_socket.mojom.h"
+#include "services/network/socket_data_pump.h"
+
+namespace net {
+class SSLClientSocket;
+class ClientSocketHandle;
+class ClientSocketFactory;
+}  // namespace net
+
+namespace network {
+
+class COMPONENT_EXPORT(NETWORK_SERVICE) TLSClientSocket
+    : public mojom::TLSClientSocket,
+      public SocketDataPump::Delegate {
+ public:
+  TLSClientSocket(mojom::TLSClientSocketRequest request,
+                  mojom::SocketObserverPtr observer,
+                  const net::NetworkTrafficAnnotationTag& traffic_annotation);
+  ~TLSClientSocket() override;
+
+  void Connect(const net::HostPortPair& host_port_pair,
+               const net::SSLConfig& ssl_config,
+               std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+               const net::SSLClientSocketContext& ssl_client_socket_context,
+               net::ClientSocketFactory* socket_factory,
+               mojom::TCPConnectedSocket::UpgradeToTLSCallback callback);
+
+ private:
+  void OnTLSConnectCompleted(int result);
+
+  // SocketDataPump::Delegate implementation.
+  void OnNetworkReadError(int net_error) override;
+  void OnNetworkWriteError(int net_error) override;
+  void OnShutdown() override;
+
+  const mojom::SocketObserverPtr observer_;
+  std::unique_ptr<SocketDataPump> socket_data_pump_;
+  std::unique_ptr<net::SSLClientSocket> socket_;
+  mojom::TCPConnectedSocket::UpgradeToTLSCallback connect_callback_;
+  const net::NetworkTrafficAnnotationTag traffic_annotation_;
+
+  DISALLOW_COPY_AND_ASSIGN(TLSClientSocket);
+};
+
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_TLS_CLIENT_SOCKET_H_
diff --git a/services/network/tls_client_socket_unittest.cc b/services/network/tls_client_socket_unittest.cc
new file mode 100644
index 0000000..d10a3a32
--- /dev/null
+++ b/services/network/tls_client_socket_unittest.cc
@@ -0,0 +1,817 @@
+// 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 <stdint.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "net/base/completion_callback.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/server_socket.h"
+#include "net/socket/socket_test_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/network/mojo_socket_test_util.h"
+#include "services/network/public/mojom/network_service.mojom.h"
+#include "services/network/socket_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network {
+
+namespace {
+
+// Message sent over the tcp connection.
+const char kMsg[] = "please start tls!";
+const size_t kMsgSize = strlen(kMsg);
+
+// Message sent over the tls connection.
+const char kSecretMsg[] = "here is secret.";
+const size_t kSecretMsgSize = strlen(kSecretMsg);
+
+class TLSClientSocketTestBase {
+ public:
+  TLSClientSocketTestBase()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO),
+        url_request_context_(true) {}
+  ~TLSClientSocketTestBase() {}
+
+ protected:
+  // Initializes the test fixture. If |use_mock_sockets|, mock client socket
+  // factory will be used.
+  void Init(bool use_mock_sockets) {
+    if (use_mock_sockets) {
+      mock_client_socket_factory_.set_enable_read_if_ready(true);
+      url_request_context_.set_client_socket_factory(
+          &mock_client_socket_factory_);
+    }
+    url_request_context_.Init();
+    factory_ = std::make_unique<SocketFactory>(nullptr /*net_log*/,
+                                               &url_request_context_);
+  }
+
+  // Reads |num_bytes| from |handle| or reads until an error occurs. Returns the
+  // bytes read as a string.
+  std::string Read(mojo::ScopedDataPipeConsumerHandle* handle,
+                   size_t num_bytes) {
+    std::string received_contents;
+    while (received_contents.size() < num_bytes) {
+      base::RunLoop().RunUntilIdle();
+      std::vector<char> buffer(num_bytes);
+      uint32_t read_size = static_cast<uint32_t>(num_bytes);
+      MojoResult result = handle->get().ReadData(buffer.data(), &read_size,
+                                                 MOJO_READ_DATA_FLAG_NONE);
+      if (result == MOJO_RESULT_SHOULD_WAIT)
+        continue;
+      if (result != MOJO_RESULT_OK)
+        return received_contents;
+      received_contents.append(buffer.data(), read_size);
+    }
+    return received_contents;
+  }
+
+  int CreateTCPConnectedSocketSync(mojom::TCPConnectedSocketRequest request,
+                                   const net::IPEndPoint& remote_addr) {
+    net::AddressList remote_addr_list(remote_addr);
+    base::RunLoop run_loop;
+    int net_error = net::ERR_FAILED;
+    factory_->CreateTCPConnectedSocket(
+        base::nullopt /* local_addr */, remote_addr_list,
+        TRAFFIC_ANNOTATION_FOR_TESTS, std::move(request),
+        pre_tls_observer()->GetObserverPtr(),
+        base::BindLambdaForTesting(
+            [&](int result,
+                mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
+                mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
+              net_error = result;
+              pre_tls_recv_handle_ = std::move(receive_pipe_handle);
+              pre_tls_send_handle_ = std::move(send_pipe_handle);
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return net_error;
+  }
+
+  void UpgradeToTLS(mojom::TCPConnectedSocket* client_socket,
+                    const net::HostPortPair& host_port_pair,
+                    mojom::TLSClientSocketRequest request,
+                    net::CompletionOnceCallback callback) {
+    client_socket->UpgradeToTLS(
+        host_port_pair,
+        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
+        std::move(request), post_tls_observer()->GetObserverPtr(),
+        base::BindOnce(
+            [](net::CompletionOnceCallback cb,
+               mojo::ScopedDataPipeConsumerHandle* consumer_handle,
+               mojo::ScopedDataPipeProducerHandle* producer_handle, int result,
+               mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
+               mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
+              *consumer_handle = std::move(receive_pipe_handle);
+              *producer_handle = std::move(send_pipe_handle);
+              std::move(cb).Run(result);
+            },
+            std::move(callback), &post_tls_recv_handle_,
+            &post_tls_send_handle_));
+  }
+
+  TestSocketObserver* pre_tls_observer() { return &pre_tls_observer_; }
+  TestSocketObserver* post_tls_observer() { return &post_tls_observer_; }
+
+  mojo::ScopedDataPipeConsumerHandle* pre_tls_recv_handle() {
+    return &pre_tls_recv_handle_;
+  }
+
+  mojo::ScopedDataPipeProducerHandle* pre_tls_send_handle() {
+    return &pre_tls_send_handle_;
+  }
+
+  mojo::ScopedDataPipeConsumerHandle* post_tls_recv_handle() {
+    return &post_tls_recv_handle_;
+  }
+
+  mojo::ScopedDataPipeProducerHandle* post_tls_send_handle() {
+    return &post_tls_send_handle_;
+  }
+
+  net::MockClientSocketFactory* mock_client_socket_factory() {
+    return &mock_client_socket_factory_;
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  // Mojo data handles obtained from CreateTCPConnectedSocket.
+  mojo::ScopedDataPipeConsumerHandle pre_tls_recv_handle_;
+  mojo::ScopedDataPipeProducerHandle pre_tls_send_handle_;
+
+  // Mojo data handles obtained from UpgradeToTLS.
+  mojo::ScopedDataPipeConsumerHandle post_tls_recv_handle_;
+  mojo::ScopedDataPipeProducerHandle post_tls_send_handle_;
+
+  net::TestURLRequestContext url_request_context_;
+  net::MockClientSocketFactory mock_client_socket_factory_;
+  std::unique_ptr<SocketFactory> factory_;
+  TestSocketObserver pre_tls_observer_;
+  TestSocketObserver post_tls_observer_;
+  mojo::StrongBindingSet<mojom::TCPServerSocket> tcp_server_socket_bindings_;
+  mojo::StrongBindingSet<mojom::TCPConnectedSocket>
+      tcp_connected_socket_bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTestBase);
+};
+
+}  // namespace
+class TLSClientSocketTest : public TLSClientSocketTestBase,
+                            public testing::Test {
+ public:
+  TLSClientSocketTest() : TLSClientSocketTestBase() {
+    Init(true /* use_mock_sockets */);
+  }
+
+  ~TLSClientSocketTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTest);
+};
+
+// Basic test to call UpgradeToTLS, and then read/write after UpgradeToTLS is
+// successful.
+TEST_F(TLSClientSocketTest, UpgradeToTLS) {
+  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, kMsg, kMsgSize, 1),
+                                  net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 0)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  uint32_t num_bytes = strlen(kMsg);
+  EXPECT_EQ(MOJO_RESULT_OK, post_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Same as the UpgradeToTLS test above, except this test calls
+// base::RunLoop().RunUntilIdle() after destroying the pre-tls data pipes.
+TEST_F(TLSClientSocketTest, ClosePipesRunUntilIdleAndUpgradeToTLS) {
+  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, kMsg, kMsgSize, 1),
+                                  net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 0)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+
+  // Call RunUntilIdle() to test the case that pipes are closed before
+  // UpgradeToTLS.
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  base::RunLoop().RunUntilIdle();
+
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  uint32_t num_bytes = strlen(kMsg);
+  EXPECT_EQ(MOJO_RESULT_OK, post_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Calling UpgradeToTLS on the same TCPConnectedSocketPtr is illegal and should
+// receive an error.
+TEST_F(TLSClientSocketTest, UpgradeToTLSTwice) {
+  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 0)};
+  net::SequencedSocketData data_provider(kReads, base::span<net::MockWrite>());
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+
+  // First UpgradeToTLS should complete successfully.
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+
+  // Second time UpgradeToTLS is called, it should fail.
+  mojom::TLSClientSocketPtr tls_socket2;
+  base::RunLoop run_loop;
+  int net_error = net::ERR_FAILED;
+  client_socket->UpgradeToTLS(
+      host_port_pair,
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
+      mojo::MakeRequest(&tls_socket2), nullptr /*observer */,
+      base::BindLambdaForTesting(
+          [&](int result,
+              mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
+              mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
+            net_error = result;
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, net_error);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Same as the UpgradeToTLS test, except this also reads and writes to the tcp
+// connection before UpgradeToTLS is called.
+TEST_F(TLSClientSocketTest, ReadWriteBeforeUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::SYNCHRONOUS, kMsg, kMsgSize, 0),
+      net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 3),
+      net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 1),
+      net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 2),
+  };
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
+
+  uint32_t num_bytes = kMsgSize;
+  EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that a read error is encountered after UpgradeToTLS completes
+// successfully.
+TEST_F(TLSClientSocketTest, ReadErrorAfterUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 1),
+      net::MockRead(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 2)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 0)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  uint32_t num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
+            post_tls_observer()->WaitForReadError());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that a read error is encountered after UpgradeToTLS completes
+// successfully.
+TEST_F(TLSClientSocketTest, WriteErrorAfterUpgradeToTLS) {
+  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 0)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 1)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  uint32_t num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
+            post_tls_observer()->WaitForWriteError());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that reading from the pre-tls data pipe is okay even after UpgradeToTLS
+// is called.
+TEST_F(TLSClientSocketTest, ReadFromPreTlsDataPipeAfterUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::ASYNC, kMsg, kMsgSize, 0),
+      net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 2),
+      net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 1)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
+
+  // Reset pre-tls receive pipe now and UpgradeToTLS should complete.
+  pre_tls_recv_handle()->reset();
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  uint32_t num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that writing to the pre-tls data pipe is okay even after UpgradeToTLS
+// is called.
+TEST_F(TLSClientSocketTest, WriteToPreTlsDataPipeAfterUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 2),
+      net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 0),
+      net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 1)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  base::RunLoop().RunUntilIdle();
+
+  uint32_t num_bytes = strlen(kMsg);
+  EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+
+  // Reset pre-tls send pipe now and UpgradeToTLS should complete.
+  pre_tls_send_handle()->reset();
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that reading from and writing to pre-tls data pipe is okay even after
+// UpgradeToTLS is called.
+TEST_F(TLSClientSocketTest, ReadAndWritePreTlsDataPipeAfterUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::ASYNC, kMsg, kMsgSize, 0),
+      net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 3),
+      net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 1),
+      net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 2)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  base::RunLoop run_loop;
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
+  uint32_t num_bytes = strlen(kMsg);
+  EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+
+  // Reset pre-tls pipes now and UpgradeToTLS should complete.
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  num_bytes = strlen(kSecretMsg);
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that a read error is encountered before UpgradeToTLS completes.
+TEST_F(TLSClientSocketTest, ReadErrorBeforeUpgradeToTLS) {
+  const net::MockRead kReads[] = {
+      net::MockRead(net::ASYNC, kMsg, kMsgSize, 0),
+      net::MockRead(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 1)};
+  net::SequencedSocketData data_provider(kReads, base::span<net::MockWrite>());
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+
+  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
+  EXPECT_EQ(net::ERR_CONNECTION_CLOSED, pre_tls_observer()->WaitForReadError());
+
+  // Reset pre-tls receive pipe now and UpgradeToTLS should complete.
+  pre_tls_recv_handle()->reset();
+  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
+  client_socket.reset();
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+// Tests that a write error is encountered before UpgradeToTLS completes.
+TEST_F(TLSClientSocketTest, WriteErrorBeforeUpgradeToTLS) {
+  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 1)};
+  const net::MockWrite kWrites[] = {
+      net::MockWrite(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 0)};
+  net::SequencedSocketData data_provider(kReads, kWrites);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  uint32_t num_bytes = strlen(kMsg);
+  EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
+                                &kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+
+  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
+            pre_tls_observer()->WaitForWriteError());
+  // Reset pre-tls send pipe now and UpgradeToTLS should complete.
+  pre_tls_send_handle()->reset();
+  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
+  client_socket.reset();
+
+  base::RunLoop().RunUntilIdle();
+  // Write failed before the mock read can be consumed.
+  EXPECT_FALSE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+class TLSClientSocketParameterizedTest
+    : public TLSClientSocketTestBase,
+      public testing::TestWithParam<net::IoMode> {
+ public:
+  TLSClientSocketParameterizedTest() : TLSClientSocketTestBase() {
+    Init(true /* use_mock_sockets*/);
+  }
+
+  ~TLSClientSocketParameterizedTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TLSClientSocketParameterizedTest);
+};
+
+INSTANTIATE_TEST_CASE_P(/* no prefix */,
+                        TLSClientSocketParameterizedTest,
+                        testing::Values(net::SYNCHRONOUS, net::ASYNC));
+
+TEST_P(TLSClientSocketParameterizedTest, MultipleWriteToTLSSocket) {
+  const int kNumIterations = 3;
+  std::vector<net::MockRead> reads;
+  std::vector<net::MockWrite> writes;
+  int sequence_number = 0;
+  net::IoMode mode = GetParam();
+  for (int j = 0; j < kNumIterations; ++j) {
+    for (size_t i = 0; i < kSecretMsgSize; ++i) {
+      writes.push_back(
+          net::MockWrite(mode, &kSecretMsg[i], 1, sequence_number++));
+    }
+    for (size_t i = 0; i < kSecretMsgSize; ++i) {
+      reads.push_back(
+          net::MockRead(net::ASYNC, &kSecretMsg[i], 1, sequence_number++));
+    }
+    if (j == kNumIterations - 1) {
+      reads.push_back(net::MockRead(mode, net::OK, sequence_number++));
+    }
+  }
+  net::SequencedSocketData data_provider(reads, writes);
+  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  net::HostPortPair host_port_pair("example.org", 443);
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), host_port_pair,
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  // Loop kNumIterations times to test that writes can follow reads, and reads
+  // can follow writes.
+  for (int j = 0; j < kNumIterations; ++j) {
+    // Write multiple times.
+    for (size_t i = 0; i < kSecretMsgSize; ++i) {
+      uint32_t num_bytes = 1;
+      EXPECT_EQ(MOJO_RESULT_OK,
+                post_tls_send_handle()->get().WriteData(
+                    &kSecretMsg[i], &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
+      // Flush the 1 byte write.
+      base::RunLoop().RunUntilIdle();
+    }
+    // Reading kSecretMsgSize should coalesce the 1-byte mock reads.
+    EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
+  }
+  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
+class TLSClientSocketTestWithEmbeddedTestServer
+    : public TLSClientSocketTestBase,
+      public testing::Test {
+ public:
+  TLSClientSocketTestWithEmbeddedTestServer() : TLSClientSocketTestBase() {
+    Init(false /* use_mock_sockets */);
+  }
+
+  ~TLSClientSocketTestWithEmbeddedTestServer() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTestWithEmbeddedTestServer);
+};
+
+TEST_F(TLSClientSocketTestWithEmbeddedTestServer, Basic) {
+  net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
+  server.RegisterRequestHandler(
+      base::BindRepeating([](const net::test_server::HttpRequest& request) {
+        if (base::StartsWith(request.relative_url, "/secret",
+                             base::CompareCase::INSENSITIVE_ASCII)) {
+          return std::unique_ptr<net::test_server::HttpResponse>(
+              new net::test_server::RawHttpResponse("HTTP/1.1 200 OK",
+                                                    "Hello There!"));
+        }
+        return std::unique_ptr<net::test_server::HttpResponse>();
+      }));
+  server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
+  ASSERT_TRUE(server.Start());
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), server.port());
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), server.host_port_pair(),
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::OK, callback.WaitForResult());
+  client_socket.reset();
+
+  const char kTestMsg[] = "GET /secret HTTP/1.1\r\n\r\n";
+  uint32_t num_bytes = strlen(kTestMsg);
+  const char kResponse[] = "HTTP/1.1 200 OK\n\n";
+  EXPECT_EQ(MOJO_RESULT_OK,
+            post_tls_send_handle()->get().WriteData(&kTestMsg, &num_bytes,
+                                                    MOJO_WRITE_DATA_FLAG_NONE));
+  EXPECT_EQ(kResponse, Read(post_tls_recv_handle(), strlen(kResponse)));
+}
+
+TEST_F(TLSClientSocketTestWithEmbeddedTestServer, ServerCertError) {
+  net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
+  server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+  ASSERT_TRUE(server.Start());
+
+  mojom::TCPConnectedSocketPtr client_socket;
+  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), server.port());
+  EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
+                         mojo::MakeRequest(&client_socket), server_addr));
+
+  pre_tls_recv_handle()->reset();
+  pre_tls_send_handle()->reset();
+  net::TestCompletionCallback callback;
+  mojom::TLSClientSocketPtr tls_socket;
+  UpgradeToTLS(client_socket.get(), server.host_port_pair(),
+               mojo::MakeRequest(&tls_socket), callback.callback());
+  ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, callback.WaitForResult());
+}
+
+}  // namespace network
diff --git a/services/network/url_request_context_builder_mojo.cc b/services/network/url_request_context_builder_mojo.cc
index 122176e..a4e4c48 100644
--- a/services/network/url_request_context_builder_mojo.cc
+++ b/services/network/url_request_context_builder_mojo.cc
@@ -39,6 +39,8 @@
     net::NetworkQualityEstimator* network_quality_estimator) {
   return NetworkContext::ApplyContextParamsToBuilder(
       this, params, quic_disabled, net_log, network_quality_estimator,
+      nullptr, /* sth_distributor */
+      nullptr, /* out_ct_tree_tracker */
       nullptr /* out_static_user_agent_settings */);
 }
 
diff --git a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
index f45eb16..97fbbe3b 100644
--- a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
+++ b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
@@ -36,6 +36,12 @@
   void SetMainThreadTaskLoadIsLow(bool main_thread_task_load_is_low) override;
   void SetPID(int64_t pid) override;
 
+  // Private implementation properties.
+  void set_private_footprint_kb(uint64_t private_footprint_kb) {
+    private_footprint_kb_ = private_footprint_kb;
+  }
+  uint64_t private_footprint_kb() const { return private_footprint_kb_; }
+
   std::set<FrameCoordinationUnitImpl*> GetFrameCoordinationUnits() const;
   std::set<PageCoordinationUnitImpl*> GetAssociatedPageCoordinationUnits()
       const;
@@ -50,6 +56,8 @@
   bool AddFrame(FrameCoordinationUnitImpl* frame_cu);
   bool RemoveFrame(FrameCoordinationUnitImpl* frame_cu);
 
+  uint64_t private_footprint_kb_ = 0u;
+
   std::set<FrameCoordinationUnitImpl*> frame_coordination_units_;
 
   DISALLOW_COPY_AND_ASSIGN(ProcessCoordinationUnitImpl);
diff --git a/services/resource_coordinator/coordination_unit/system_coordination_unit_impl.cc b/services/resource_coordinator/coordination_unit/system_coordination_unit_impl.cc
index 464a43d..acd8a00 100644
--- a/services/resource_coordinator/coordination_unit/system_coordination_unit_impl.cc
+++ b/services/resource_coordinator/coordination_unit/system_coordination_unit_impl.cc
@@ -29,7 +29,8 @@
       ProcessCoordinationUnitImpl::GetAllProcessCoordinationUnits();
 
   for (const auto& measurement : measurement_batch->measurements) {
-    for (ProcessCoordinationUnitImpl* process : processes) {
+    for (auto it = processes.begin(); it != processes.end(); ++it) {
+      ProcessCoordinationUnitImpl* process = *it;
       int64_t process_pid;
       // TODO(siggi): This seems pretty silly - we're going O(N^2) in processes
       //     here, and going through a relatively expensive accessor for the
@@ -37,12 +38,21 @@
       if (process->GetProperty(mojom::PropertyType::kPID, &process_pid) &&
           static_cast<base::ProcessId>(process_pid) == measurement->pid) {
         process->SetCPUUsage(measurement->cpu_usage);
+        process->set_private_footprint_kb(measurement->private_footprint_kb);
 
+        // Remove found processes.
+        processes.erase(it);
         break;
       }
     }
   }
 
+  // Clear processes we didn't get data for.
+  for (ProcessCoordinationUnitImpl* process : processes) {
+    process->SetCPUUsage(0.0);
+    process->set_private_footprint_kb(0);
+  }
+
   // Fire the end update signal.
   OnProcessCPUUsageReady();
 }
diff --git a/services/resource_coordinator/coordination_unit/system_coordination_unit_impl_unittest.cc b/services/resource_coordinator/coordination_unit/system_coordination_unit_impl_unittest.cc
index b9082a12..bf07279 100644
--- a/services/resource_coordinator/coordination_unit/system_coordination_unit_impl_unittest.cc
+++ b/services/resource_coordinator/coordination_unit/system_coordination_unit_impl_unittest.cc
@@ -110,10 +110,34 @@
   EXPECT_TRUE(cu_graph.process->GetProperty(mojom::PropertyType::kCPUUsage,
                                             &cpu_usage));
   EXPECT_EQ(1000, cpu_usage);
+  EXPECT_EQ(1u, cu_graph.process->private_footprint_kb());
 
   EXPECT_TRUE(cu_graph.other_process->GetProperty(
       mojom::PropertyType::kCPUUsage, &cpu_usage));
   EXPECT_EQ(2000, cpu_usage);
+  EXPECT_EQ(2u, cu_graph.other_process->private_footprint_kb());
+
+  // Now test that a measurement batch that leaves out a process clears the
+  // properties of that process.
+  batch = mojom::ProcessResourceMeasurementBatch::New();
+  mojom::ProcessResourceMeasurementPtr measurement =
+      mojom::ProcessResourceMeasurement::New();
+  measurement->pid = 1;
+  measurement->cpu_usage = 30.0;
+  measurement->private_footprint_kb = 30u;
+  batch->measurements.push_back(std::move(measurement));
+
+  cu_graph.system->DistributeMeasurementBatch(std::move(batch));
+
+  EXPECT_TRUE(cu_graph.process->GetProperty(mojom::PropertyType::kCPUUsage,
+                                            &cpu_usage));
+  EXPECT_EQ(30000, cpu_usage);
+  EXPECT_EQ(30u, cu_graph.process->private_footprint_kb());
+
+  EXPECT_TRUE(cu_graph.other_process->GetProperty(
+      mojom::PropertyType::kCPUUsage, &cpu_usage));
+  EXPECT_EQ(0, cpu_usage);
+  EXPECT_EQ(0u, cu_graph.other_process->private_footprint_kb());
 }
 
 }  // namespace resource_coordinator
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
index e3a37d0..3c8ccb9 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <memory>
 
+#include "base/debug/elf_reader_linux.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/format_macros.h"
@@ -15,6 +16,9 @@
 #include "build/build_config.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 
+// Symbol with virtual address of the start of ELF header of the current binary.
+extern char __ehdr_start;
+
 namespace memory_instrumentation {
 
 namespace {
@@ -64,8 +68,9 @@
 
   if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
              &region->start_address, &end_addr, protection_flags,
-             mapped_file) != 4)
+             mapped_file) != 4) {
     return false;
+  }
 
   if (end_addr > region->start_address) {
     region->size_in_bytes = end_addr - region->start_address;
@@ -93,6 +98,19 @@
   base::TrimWhitespaceASCII(region->mapped_file, base::TRIM_ALL,
                             &region->mapped_file);
 
+  // Build ID is needed to symbolize heap profiles, and is generated only on
+  // official builds. Build ID is only added for the current library (chrome)
+  // since it is racy to read other libraries which can be unmapped any time.
+#if defined(OFFICIAL_BUILD)
+  uintptr_t addr = reinterpret_cast<uintptr_t>(&ParseSmapsHeader);
+  if (addr >= region->start_address && addr < end_addr) {
+    base::Optional<std::string> buildid =
+        base::debug::ReadElfBuildId(&__ehdr_start);
+    if (buildid)
+      region->module_debugid = buildid.value();
+  }
+#endif  // defined(OFFICIAL_BUILD)
+
   return res;
 }
 
diff --git a/services/service_manager/public/cpp/standalone_service/main.cc b/services/service_manager/public/cpp/standalone_service/main.cc
index 61c3b47..9f3e10a 100644
--- a/services/service_manager/public/cpp/standalone_service/main.cc
+++ b/services/service_manager/public/cpp/standalone_service/main.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/process/launch.h"
 #include "base/task_scheduler/task_scheduler.h"
+#include "build/build_config.h"
 #include "services/service_manager/public/cpp/standalone_service/standalone_service.h"
 #include "services/service_manager/public/cpp/standalone_service/switches.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
@@ -40,7 +41,7 @@
   base::AtExitManager at_exit;
   base::CommandLine::Init(argc, argv);
 
-#if !defined(OFFICIAL_BIULD) && defined(OS_WIN)
+#if !defined(OFFICIAL_BUILD) && defined(OS_WIN)
   base::RouteStdioToConsole(false);
 #endif
 
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index 0704695..c774956 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -169,6 +169,9 @@
   // The window's title. Maps to aura::client::kTitleKey. Type: mojom::String
   const string kWindowTitle_Property = "prop:window-title";
 
+  // A boolean determining whether to show the window's title.
+  const string kWindowTitleShown_Property = "prop:window-title-shown";
+
   // End long lived properties. ------------------------------------------------
 
   // Called immediately when the WindowManager is obtained.
diff --git a/services/ui/service.h b/services/ui/service.h
index be2dc70..2e46cd2 100644
--- a/services/ui/service.h
+++ b/services/ui/service.h
@@ -75,6 +75,8 @@
 class Service : public service_manager::Service,
                 public ws::WindowServerDelegate {
  public:
+  // TODO(jamescook): Audit these. Some may be unused after the elimination of
+  // "mus" mode.
   struct InitParams {
     InitParams();
     ~InitParams();
diff --git a/services/ui/ws2/BUILD.gn b/services/ui/ws2/BUILD.gn
index ea5a19cb..f3a792a7 100644
--- a/services/ui/ws2/BUILD.gn
+++ b/services/ui/ws2/BUILD.gn
@@ -10,32 +10,30 @@
 import("//services/service_manager/public/tools/test/service_test.gni")
 
 component("lib") {
+  public = [
+    "gpu_support.h",
+    "ids.h",
+    "window_service.h",
+    "window_service_client.h",
+    "window_service_client_binding.h",
+    "window_service_delegate.h",
+    "window_tree_factory.h",
+  ]
   sources = [
     "client_change.cc",
     "client_change.h",
     "client_change_tracker.cc",
     "client_change_tracker.h",
-
-    # TODO: client_root should be internal and moved to a different target.
     "client_root.cc",
     "client_root.h",
-    "gpu_support.h",
-    "ids.h",
-
-    # TODO: window_data should be internal and moved to a different target.
-    "window_data.cc",
-    "window_data.h",
+    "client_window.cc",
+    "client_window.h",
     "window_host_frame_sink_client.cc",
     "window_host_frame_sink_client.h",
     "window_service.cc",
-    "window_service.h",
     "window_service_client.cc",
-    "window_service_client.h",
     "window_service_client_binding.cc",
-    "window_service_client_binding.h",
-    "window_service_delegate.h",
     "window_tree_factory.cc",
-    "window_tree_factory.h",
   ]
 
   public_deps = [
diff --git a/services/ui/ws2/client_root.cc b/services/ui/ws2/client_root.cc
index 9cf879b..83c06cf 100644
--- a/services/ui/ws2/client_root.cc
+++ b/services/ui/ws2/client_root.cc
@@ -6,7 +6,7 @@
 
 #include "services/ui/ws2/client_change.h"
 #include "services/ui/ws2/client_change_tracker.h"
-#include "services/ui/ws2/window_data.h"
+#include "services/ui/ws2/client_window.h"
 #include "services/ui/ws2/window_service_client.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/window.h"
@@ -21,7 +21,7 @@
     : window_service_client_(window_service_client),
       window_(window),
       is_top_level_(is_top_level) {
-  WindowData::GetMayBeNull(window)->set_embedded_window_service_client(
+  ClientWindow::GetMayBeNull(window)->set_embedded_window_service_client(
       window_service_client);
   window_->AddObserver(this);
   // TODO: wire up gfx::Insets() correctly below. See usage in
@@ -36,14 +36,14 @@
 }
 
 ClientRoot::~ClientRoot() {
-  WindowData::GetMayBeNull(window_)->set_embedded_window_service_client(
+  ClientWindow::GetMayBeNull(window_)->set_embedded_window_service_client(
       nullptr);
   window_->RemoveObserver(this);
 }
 
 void ClientRoot::FrameSinkIdChanged() {
   window_->SetEmbedFrameSinkId(
-      WindowData::GetMayBeNull(window_)->frame_sink_id());
+      ClientWindow::GetMayBeNull(window_)->frame_sink_id());
   UpdatePrimarySurfaceId();
 }
 
diff --git a/services/ui/ws2/window_data.cc b/services/ui/ws2/client_window.cc
similarity index 66%
rename from services/ui/ws2/window_data.cc
rename to services/ui/ws2/client_window.cc
index 228b95b46..c605ab2 100644
--- a/services/ui/ws2/window_data.cc
+++ b/services/ui/ws2/client_window.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 "services/ui/ws2/window_data.h"
+#include "services/ui/ws2/client_window.h"
 
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "services/ui/ws2/window_host_frame_sink_client.h"
@@ -10,35 +10,35 @@
 #include "ui/aura/window.h"
 #include "ui/compositor/compositor.h"
 
-DEFINE_UI_CLASS_PROPERTY_TYPE(ui::ws2::WindowData*);
+DEFINE_UI_CLASS_PROPERTY_TYPE(ui::ws2::ClientWindow*);
 
 namespace ui {
 namespace ws2 {
 namespace {
-DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ui::ws2::WindowData,
-                                   kWindowDataKey,
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ui::ws2::ClientWindow,
+                                   kClientWindowKey,
                                    nullptr);
 }  // namespace
 
-WindowData::~WindowData() = default;
+ClientWindow::~ClientWindow() = default;
 
 // static
-WindowData* WindowData::Create(aura::Window* window,
-                               WindowServiceClient* client,
-                               const viz::FrameSinkId& frame_sink_id) {
+ClientWindow* ClientWindow::Create(aura::Window* window,
+                                   WindowServiceClient* client,
+                                   const viz::FrameSinkId& frame_sink_id) {
   DCHECK(!GetMayBeNull(window));
   // Owned by |window|.
-  WindowData* data = new WindowData(window, client, frame_sink_id);
-  window->SetProperty(kWindowDataKey, data);
-  return data;
+  ClientWindow* client_window = new ClientWindow(window, client, frame_sink_id);
+  window->SetProperty(kClientWindowKey, client_window);
+  return client_window;
 }
 
 // static
-const WindowData* WindowData::GetMayBeNull(const aura::Window* window) {
-  return window->GetProperty(kWindowDataKey);
+const ClientWindow* ClientWindow::GetMayBeNull(const aura::Window* window) {
+  return window->GetProperty(kClientWindowKey);
 }
 
-void WindowData::SetFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
+void ClientWindow::SetFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
   frame_sink_id_ = frame_sink_id;
   viz::HostFrameSinkManager* host_frame_sink_manager =
       aura::Env::GetInstance()
@@ -61,7 +61,7 @@
   window_host_frame_sink_client_->OnFrameSinkIdChanged();
 }
 
-void WindowData::AttachCompositorFrameSink(
+void ClientWindow::AttachCompositorFrameSink(
     viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
     viz::mojom::CompositorFrameSinkClientPtr client) {
   viz::HostFrameSinkManager* host_frame_sink_manager =
@@ -72,9 +72,9 @@
       frame_sink_id_, std::move(compositor_frame_sink), std::move(client));
 }
 
-WindowData::WindowData(aura::Window* window,
-                       WindowServiceClient* client,
-                       const viz::FrameSinkId& frame_sink_id)
+ClientWindow::ClientWindow(aura::Window* window,
+                           WindowServiceClient* client,
+                           const viz::FrameSinkId& frame_sink_id)
     : window_(window),
       owning_window_service_client_(client),
       frame_sink_id_(frame_sink_id) {}
diff --git a/services/ui/ws2/window_data.h b/services/ui/ws2/client_window.h
similarity index 75%
rename from services/ui/ws2/window_data.h
rename to services/ui/ws2/client_window.h
index f71943a..e2996ac 100644
--- a/services/ui/ws2/window_data.h
+++ b/services/ui/ws2/client_window.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_UI_WS2_WINDOW_DATA_H_
-#define SERVICES_UI_WS2_WINDOW_DATA_H_
+#ifndef SERVICES_UI_WS2_CLIENT_WINDOW_H_
+#define SERVICES_UI_WS2_CLIENT_WINDOW_H_
 
 #include "base/component_export.h"
 #include "base/macros.h"
@@ -22,24 +22,24 @@
 class WindowServiceClient;
 
 // Tracks any state associated with an aura::Window for the WindowService.
-// WindowData is created for every window created at the request of a client,
+// ClientWindow is created for every window created at the request of a client,
 // including the root window of ClientRoots.
-class COMPONENT_EXPORT(WINDOW_SERVICE) WindowData {
+class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
  public:
-  ~WindowData();
+  ~ClientWindow();
 
-  // Creates a new WindowData. The lifetime of the WindowData is tied to that
-  // of the Window (the Window ends up owning the WindowData).
-  static WindowData* Create(aura::Window* window,
-                            WindowServiceClient* client,
-                            const viz::FrameSinkId& frame_sink_id);
+  // Creates a new ClientWindow. The lifetime of the ClientWindow is tied to
+  // that of the Window (the Window ends up owning the ClientWindow).
+  static ClientWindow* Create(aura::Window* window,
+                              WindowServiceClient* client,
+                              const viz::FrameSinkId& frame_sink_id);
 
-  // Returns the WindowData associated with a window, null if not created yet.
-  static WindowData* GetMayBeNull(aura::Window* window) {
-    return const_cast<WindowData*>(
+  // Returns the ClientWindow associated with a window, null if not created yet.
+  static ClientWindow* GetMayBeNull(aura::Window* window) {
+    return const_cast<ClientWindow*>(
         GetMayBeNull(const_cast<const aura::Window*>(window)));
   }
-  static const WindowData* GetMayBeNull(const aura::Window* window);
+  static const ClientWindow* GetMayBeNull(const aura::Window* window);
 
   WindowServiceClient* owning_window_service_client() {
     return owning_window_service_client_;
@@ -66,9 +66,9 @@
       viz::mojom::CompositorFrameSinkClientPtr client);
 
  private:
-  WindowData(aura::Window*,
-             WindowServiceClient* client,
-             const viz::FrameSinkId& frame_sink_id);
+  ClientWindow(aura::Window*,
+               WindowServiceClient* client,
+               const viz::FrameSinkId& frame_sink_id);
 
   aura::Window* window_;
 
@@ -99,10 +99,10 @@
   // window.
   std::unique_ptr<WindowHostFrameSinkClient> window_host_frame_sink_client_;
 
-  DISALLOW_COPY_AND_ASSIGN(WindowData);
+  DISALLOW_COPY_AND_ASSIGN(ClientWindow);
 };
 
 }  // namespace ws2
 }  // namespace ui
 
-#endif  // SERVICES_UI_WS2_WINDOW_DATA_H_
+#endif  // SERVICES_UI_WS2_CLIENT_WINDOW_H_
diff --git a/services/ui/ws2/window_host_frame_sink_client.cc b/services/ui/ws2/window_host_frame_sink_client.cc
index b7dd830..df2196e8 100644
--- a/services/ui/ws2/window_host_frame_sink_client.cc
+++ b/services/ui/ws2/window_host_frame_sink_client.cc
@@ -5,7 +5,6 @@
 #include "services/ui/ws2/window_host_frame_sink_client.h"
 
 #include "base/logging.h"
-#include "services/ui/ws2/window_data.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 
 namespace ui {
diff --git a/services/ui/ws2/window_service.cc b/services/ui/ws2/window_service.cc
index cc624d2..e40f3fb 100644
--- a/services/ui/ws2/window_service.cc
+++ b/services/ui/ws2/window_service.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
+#include "services/ui/ws2/client_window.h"
 #include "services/ui/ws2/gpu_support.h"
-#include "services/ui/ws2/window_data.h"
 #include "services/ui/ws2/window_service_client.h"
 #include "services/ui/ws2/window_service_delegate.h"
 #include "services/ui/ws2/window_tree_factory.h"
@@ -26,16 +26,16 @@
 
 WindowService::~WindowService() {}
 
-WindowData* WindowService::GetWindowDataForWindowCreateIfNecessary(
+ClientWindow* WindowService::GetClientWindowForWindowCreateIfNecessary(
     aura::Window* window) {
-  WindowData* data = WindowData::GetMayBeNull(window);
-  if (data)
-    return data;
+  ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
+  if (client_window)
+    return client_window;
 
   const viz::FrameSinkId frame_sink_id =
       ClientWindowId(kWindowServerClientId, next_window_id_++);
   CHECK_NE(0u, next_window_id_);
-  return WindowData::Create(window, nullptr, frame_sink_id);
+  return ClientWindow::Create(window, nullptr, frame_sink_id);
 }
 
 std::unique_ptr<WindowServiceClient> WindowService::CreateWindowServiceClient(
diff --git a/services/ui/ws2/window_service.h b/services/ui/ws2/window_service.h
index 1e9789d0..ed4e72bf 100644
--- a/services/ui/ws2/window_service.h
+++ b/services/ui/ws2/window_service.h
@@ -29,8 +29,8 @@
 
 namespace ws2 {
 
+class ClientWindow;
 class GpuSupport;
-class WindowData;
 class WindowServiceClient;
 class WindowServiceDelegate;
 class WindowTreeFactory;
@@ -47,8 +47,8 @@
                 std::unique_ptr<GpuSupport> gpu_support);
   ~WindowService() override;
 
-  // Gets the WindowData for |window|, creating if necessary.
-  WindowData* GetWindowDataForWindowCreateIfNecessary(aura::Window* window);
+  // Gets the ClientWindow for |window|, creating if necessary.
+  ClientWindow* GetClientWindowForWindowCreateIfNecessary(aura::Window* window);
 
   // Creates a new WindowServiceClient, caller must call one of the Init()
   // functions on the returned object.
diff --git a/services/ui/ws2/window_service_client.cc b/services/ui/ws2/window_service_client.cc
index 237a520..1dc7efe 100644
--- a/services/ui/ws2/window_service_client.cc
+++ b/services/ui/ws2/window_service_client.cc
@@ -12,7 +12,7 @@
 #include "services/ui/ws2/client_change.h"
 #include "services/ui/ws2/client_change_tracker.h"
 #include "services/ui/ws2/client_root.h"
-#include "services/ui/ws2/window_data.h"
+#include "services/ui/ws2/client_window.h"
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_client_binding.h"
 #include "services/ui/ws2/window_service_delegate.h"
@@ -40,10 +40,10 @@
 
 void WindowServiceClient::InitForEmbed(aura::Window* root,
                                        mojom::WindowTreePtr window_tree_ptr) {
-  // Force WindowData to be created for |root|.
-  WindowData* window_data =
-      window_service_->GetWindowDataForWindowCreateIfNecessary(root);
-  const ClientWindowId client_window_id = window_data->frame_sink_id();
+  // Force ClientWindow to be created for |root|.
+  ClientWindow* client_window =
+      window_service_->GetClientWindowForWindowCreateIfNecessary(root);
+  const ClientWindowId client_window_id = client_window->frame_sink_id();
   AddWindowToKnownWindows(root, client_window_id);
 
   CreateClientRoot(root, std::move(window_tree_ptr));
@@ -78,9 +78,9 @@
     mojom::WindowTreePtr window_tree) {
   OnWillBecomeClientRootWindow(window);
 
-  WindowData* window_data = WindowData::GetMayBeNull(window);
-  DCHECK(window_data);
-  const ClientWindowId client_window_id = window_data->frame_sink_id();
+  ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
+  DCHECK(client_window);
+  const ClientWindowId client_window_id = client_window->frame_sink_id();
 
   const bool for_embed = window_tree.is_bound();
 
@@ -103,7 +103,7 @@
 
     // Reset the frame sink id locally (after calling OnEmbed()). This is
     // needed so that the id used by the client matches the id used locally.
-    window_data->SetFrameSinkId(ClientWindowId(client_id_, 0));
+    client_window->SetFrameSinkId(ClientWindowId(client_id_, 0));
   }
 
   // TODO(sky): centralize FrameSinkId management.
@@ -114,7 +114,7 @@
     // TODO(sky): centralize FrameSinkId management.
     window_tree_client_->OnFrameSinkIdAllocated(
         ClientWindowIdToTransportId(client_window_id),
-        window_data->frame_sink_id());
+        client_window->frame_sink_id());
   }
 
   return client_root;
@@ -159,16 +159,16 @@
 
   if (reason == DeleteClientRootReason::kUnembed) {
     // Notify the owner of the window it no longer has a client embedded in it.
-    WindowData* window_data = WindowData::GetMayBeNull(window);
-    if (window_data->owning_window_service_client() &&
-        window_data->owning_window_service_client() != this) {
-      // ClientRoots always trigger creation of a WindowData, so |window_data|
-      // must exist at this point.
-      DCHECK(window_data);
-      window_data->owning_window_service_client()
+    ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
+    if (client_window->owning_window_service_client() &&
+        client_window->owning_window_service_client() != this) {
+      // ClientRoots always trigger creation of a ClientWindow, so
+      // |client_window| must exist at this point.
+      DCHECK(client_window);
+      client_window->owning_window_service_client()
           ->window_tree_client_->OnEmbeddedAppDisconnected(
-              window_data->owning_window_service_client()->TransportIdForWindow(
-                  window));
+              client_window->owning_window_service_client()
+                  ->TransportIdForWindow(window));
     }
   }
 }
@@ -215,10 +215,10 @@
 
 bool WindowServiceClient::IsWindowRootOfAnotherClient(
     aura::Window* window) const {
-  WindowData* window_data = WindowData::GetMayBeNull(window);
-  return window_data &&
-         window_data->embedded_window_service_client() != nullptr &&
-         window_data->embedded_window_service_client() != this;
+  ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
+  return client_window &&
+         client_window->embedded_window_service_client() != nullptr &&
+         client_window->embedded_window_service_client() != this;
 }
 
 aura::Window* WindowServiceClient::AddClientCreatedWindow(
@@ -226,7 +226,7 @@
     std::unique_ptr<aura::Window> window_ptr) {
   aura::Window* window = window_ptr.get();
   client_created_windows_.insert(std::move(window_ptr));
-  WindowData::Create(window, this, id);
+  ClientWindow::Create(window, this, id);
   AddWindowToKnownWindows(window, id);
   return window;
 }
@@ -320,31 +320,31 @@
     parent = nullptr;
   if (!IsWindowKnown(transient_parent))
     transient_parent = nullptr;
-  mojom::WindowDataPtr window_data(mojom::WindowData::New());
-  window_data->parent_id =
+  mojom::WindowDataPtr client_window(mojom::WindowData::New());
+  client_window->parent_id =
       parent ? TransportIdForWindow(parent) : kInvalidTransportId;
-  window_data->window_id =
+  client_window->window_id =
       window ? TransportIdForWindow(window) : kInvalidTransportId;
-  window_data->transient_parent_id =
+  client_window->transient_parent_id =
       transient_parent ? TransportIdForWindow(transient_parent)
                        : kInvalidTransportId;
-  window_data->bounds = window->bounds();
+  client_window->bounds = window->bounds();
 
   // TODO: use property mapping.
-  window_data->visible = window->TargetVisibility();
-  return window_data;
+  client_window->visible = window->TargetVisibility();
+  return client_window;
 }
 
 void WindowServiceClient::OnWillBecomeClientRootWindow(aura::Window* window) {
   DCHECK(window);
 
   // Only one client may be embedded in a window at a time.
-  WindowData* window_data =
-      window_service_->GetWindowDataForWindowCreateIfNecessary(window);
-  if (window_data->embedded_window_service_client()) {
-    window_data->embedded_window_service_client()->DeleteClientRootWithRoot(
+  ClientWindow* client_window =
+      window_service_->GetClientWindowForWindowCreateIfNecessary(window);
+  if (client_window->embedded_window_service_client()) {
+    client_window->embedded_window_service_client()->DeleteClientRootWithRoot(
         window);
-    DCHECK(!window_data->embedded_window_service_client());
+    DCHECK(!client_window->embedded_window_service_client());
   }
 
   // Because a new client is being embedded all existing children are removed.
@@ -670,7 +670,7 @@
   top_level_ptr->set_owned_by_parent(false);
   aura::Window* top_level =
       AddClientCreatedWindow(client_window_id, std::move(top_level_ptr));
-  WindowData::GetMayBeNull(top_level)->SetFrameSinkId(client_window_id);
+  ClientWindow::GetMayBeNull(top_level)->SetFrameSinkId(client_window_id);
   const int64_t display_id =
       display::Screen::GetScreen()->GetDisplayNearestWindow(top_level).id();
   // This passes null for the mojom::WindowTreePtr because the client has
@@ -775,19 +775,20 @@
     DVLOG(1) << "AttachCompositorFrameSink failed (invalid window id)";
     return;
   }
-  WindowData* window_data = WindowData::GetMayBeNull(window);
+  ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
   // If this isn't called on the root, then only allow it if there is not
   // another client embedded in the window.
-  const bool allow = IsClientRootWindow(window) ||
-                     (IsClientCreatedWindow(window) &&
-                      window_data->embedded_window_service_client() == nullptr);
+  const bool allow =
+      IsClientRootWindow(window) ||
+      (IsClientCreatedWindow(window) &&
+       client_window->embedded_window_service_client() == nullptr);
   if (!allow) {
     DVLOG(1) << "AttachCompositorFrameSink failed (policy disallowed)";
     return;
   }
 
-  window_data->AttachCompositorFrameSink(std::move(compositor_frame_sink),
-                                         std::move(client));
+  client_window->AttachCompositorFrameSink(std::move(compositor_frame_sink),
+                                           std::move(client));
 }
 
 void WindowServiceClient::AddWindow(uint32_t change_id,
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 1cca5c9..f45a941 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -73,6 +73,11 @@
       "SKIA_DLL",
       "GR_GL_IGNORE_ES3_MSAA=0",
     ]
+    if (is_win) {
+      defines += [ "SKCMS_API=__declspec(dllexport)" ]
+    } else {
+      defines += [ "SKCMS_API=__attribute__((visibility(\"default\")))" ]
+    }
   }
 
   if (skia_support_gpu) {
@@ -144,7 +149,10 @@
   # TODO: remove after Skia shader relocation (https://skia-review.googlesource.com/c/17927)
   include_dirs += [ "//third_party/skia/src/effects/gradients" ]
 
-  defines = [ "SK_USE_SKCMS" ]
+  defines = [
+    "SK_USE_SKCMS",
+    "SK_IGNORE_MASK_FILTER_GLYPH_FIX",
+  ]
 
   if (!is_ios && !use_system_freetype) {
     defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
diff --git a/storage/browser/database/vfs_backend.cc b/storage/browser/database/vfs_backend.cc
index 411872f8..8be20f2 100644
--- a/storage/browser/database/vfs_backend.cc
+++ b/storage/browser/database/vfs_backend.cc
@@ -126,7 +126,7 @@
     return SQLITE_IOERR_DELETE;
 
   int error_code = SQLITE_OK;
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
   if (sync_dir) {
     base::File dir(file_path.DirName(), base::File::FLAG_READ);
     if (dir.IsValid()) {
@@ -144,7 +144,7 @@
 uint32_t VfsBackend::GetFileAttributes(const base::FilePath& file_path) {
 #if defined(OS_WIN)
   uint32_t attributes = ::GetFileAttributes(file_path.value().c_str());
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   uint32_t attributes = 0;
   if (!access(file_path.value().c_str(), R_OK))
     attributes |= static_cast<uint32_t>(R_OK);
diff --git a/storage/common/fileapi/file_system_util.cc b/storage/common/fileapi/file_system_util.cc
index 8168b5d..35c99ec 100644
--- a/storage/common/fileapi/file_system_util.cc
+++ b/storage/common/fileapi/file_system_util.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "net/base/escape.h"
 #include "net/base/net_errors.h"
 #include "storage/common/database/database_identifier.h"
@@ -324,7 +325,7 @@
 std::string FilePathToString(const base::FilePath& file_path) {
 #if defined(OS_WIN)
   return base::UTF16ToUTF8(file_path.value());
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return file_path.value();
 #endif
 }
@@ -332,7 +333,7 @@
 base::FilePath StringToFilePath(const std::string& file_path_string) {
 #if defined(OS_WIN)
   return base::FilePath(base::UTF8ToUTF16(file_path_string));
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return base::FilePath(file_path_string);
 #endif
 }
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index c5cacdc..376211d 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -13155,5 +13155,2457 @@
         "test": "chrome_public_test_vr_apk"
       }
     ]
+  },
+  "android-kitkat-arm-rel": {
+    "additional_compile_targets": [
+      "cronet_test_instrumentation_apk",
+      "monochrome_static_initializers"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "android_webview_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "android_webview_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "base_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "blink_heap_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "boringssl_crypto_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "boringssl_ssl_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "breakpad_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "breakpad_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cacheinvalidation_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "capture_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "cc_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 900,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_test_apk"
+          ],
+          "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": 1500,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "chrome_public_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_test_vr_apk"
+          ],
+          "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"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "chrome_public_test_vr_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_sync_shell_test_apk"
+          ],
+          "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": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "chrome_sync_shell_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "components_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "components_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 900,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "components_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_browsertests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 1500,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 8
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_shell_test_apk"
+          ],
+          "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": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "content_shell_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "content_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "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": "content_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "crypto_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "device_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "display_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "events_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gfx_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 900,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gin_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gl_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gl_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "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": "gl_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "google_apis_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "gpu_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 960,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ipc_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "jingle_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "latency_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "libjingle_xmpp_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "media_blink_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "media_service_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "media_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 900,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "midi_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "mojo_test_apk"
+          ],
+          "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": 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_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--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": 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"
+        },
+        "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": 1800,
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 3
+        },
+        "test": "net_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "remoting_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"
+            }
+          ],
+          "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": "remoting_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "sandbox_linux_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "service_manager_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"
+            }
+          ],
+          "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": "service_manager_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "services_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "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": "services_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "skia_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "sql_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "storage_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_android_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "ui_android_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_base_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "ui_touch_selection_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "hard_timeout": 900,
+          "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": "unit_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "url_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "viz_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "vr_common_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "vr_common_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "vr_pixeltests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "vr_pixeltests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webkit_unit_tests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "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": "webkit_unit_tests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_instrumentation_test_apk"
+          ],
+          "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"
+            }
+          ],
+          "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": "webview_instrumentation_test_apk"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "wtf_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "monochrome_apk_checker",
+        "name": "monochrome_apk_checker",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "KTU84P",
+              "device_type": "hammerhead"
+            }
+          ]
+        }
+      }
+    ],
+    "junit_tests": [
+      {
+        "test": "base_junit_tests"
+      },
+      {
+        "test": "chrome_junit_tests"
+      },
+      {
+        "test": "components_background_task_scheduler_junit_tests"
+      },
+      {
+        "test": "components_gcm_driver_junit_tests"
+      },
+      {
+        "test": "components_invalidation_impl_junit_tests"
+      },
+      {
+        "test": "components_policy_junit_tests"
+      },
+      {
+        "test": "components_signin_junit_tests"
+      },
+      {
+        "test": "components_variations_junit_tests"
+      },
+      {
+        "test": "components_web_restrictions_junit_tests"
+      },
+      {
+        "test": "content_junit_tests"
+      },
+      {
+        "test": "device_junit_tests"
+      },
+      {
+        "test": "junit_unit_tests"
+      },
+      {
+        "test": "media_base_junit_tests"
+      },
+      {
+        "test": "net_junit_tests"
+      },
+      {
+        "test": "service_junit_tests"
+      },
+      {
+        "test": "ui_junit_tests"
+      },
+      {
+        "test": "webapk_client_junit_tests"
+      },
+      {
+        "test": "webapk_shell_apk_junit_tests"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index c13f253..e3a2916 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -2694,5 +2694,1371 @@
         "test": "services_unittests"
       }
     ]
+  },
+  "linux-xenial-rel": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "accessibility_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "app_shell_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "aura_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "battor_agent_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_heap_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_platform_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "boringssl_crypto_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "boringssl_ssl_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/viz.browser_tests.filter"
+        ],
+        "name": "viz_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cacheinvalidation_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "capture_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cast_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cc_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chrome_app_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "chromedriver_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_components_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "components_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "compositor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/site-per-process.content_browsertests.filter"
+        ],
+        "name": "site_per_process_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/viz.content_browsertests.filter"
+        ],
+        "name": "viz_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "args": [
+          "--enable-features=VizDisplayCompositor",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/viz.content_unittests.filter"
+        ],
+        "name": "viz_content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cronet_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "cronet_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "crypto_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "dbus_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "device_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "display_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "events_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "args": [
+          "--site-per-process"
+        ],
+        "name": "site_per_process_extensions_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "extensions_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "filesystem_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gcm_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gfx_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gin_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gn_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "google_apis_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "gpu_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "headless_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "headless_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 2
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ipc_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "keyboard_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "latency_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "leveldb_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "libjingle_xmpp_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_blink_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_service_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "media_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "message_center_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "midi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "mojo_core_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "mojo_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "nacl_loader_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "native_theme_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "net_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "pdf_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ppapi_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "printing_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sandbox_linux_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "service_manager_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "services_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "skia_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "snapshot_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sql_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "storage_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_sync_integration_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "sync_integration_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "traffic_annotation_auditor_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ui_base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "ui_touch_selection_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "args": [
+          "--disable-site-isolation-trials"
+        ],
+        "name": "not_site_per_process_unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "url_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "views_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "viz_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "vr_common_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "vr_pixeltests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "webkit_unit_tests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "wm_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "wtf_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--test-type=integration"
+        ],
+        "isolate_name": "chromedriver_py_tests",
+        "name": "chromedriver_py_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "content_shell_crash_test",
+        "name": "content_shell_crash_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "devtools_closure_compile",
+        "name": "devtools_closure_compile",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "devtools_eslint",
+        "name": "devtools_eslint",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "metrics_python_tests",
+        "name": "metrics_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "--additional-driver-flag",
+          "--site-per-process",
+          "--additional-driver-flag",
+          "--isolate-origins=http://www.web-platform.test:8001/,http://www1.web-platform.test:8001/,http://www2.web-platform.test:8001/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001/,http://xn--lve-6lad.web-platform.test:8001/,http://www.web-platform.test:8081/,http://www1.web-platform.test:8081/,http://www2.web-platform.test:8081/,http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8081/,http://xn--lve-6lad.web-platform.test:8081/,https://www.web-platform.test:8444/,https://www1.web-platform.test:8444/,https://www2.web-platform.test:8444/,https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8444/,https://xn--lve-6lad.web-platform.test:8444/",
+          "--additional-expectations",
+          "src/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "site_per_process_webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 10
+        }
+      },
+      {
+        "isolate_name": "telemetry_gpu_unittests",
+        "name": "telemetry_gpu_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "isolate_name": "telemetry_perf_unittests",
+        "name": "telemetry_perf_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "hard_timeout": 960,
+          "shards": 12
+        }
+      },
+      {
+        "args": [
+          "--jobs=1"
+        ],
+        "isolate_name": "telemetry_unittests",
+        "name": "telemetry_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 4
+        }
+      },
+      {
+        "isolate_name": "views_perftests",
+        "name": "views_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "--zero-tests-executed-ok"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "only_retry_failed_tests": true,
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 12
+        }
+      },
+      {
+        "isolate_name": "webkit_python_tests",
+        "name": "webkit_python_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        }
+      }
+    ],
+    "scripts": [
+      {
+        "name": "checkdeps",
+        "script": "checkdeps.py"
+      },
+      {
+        "name": "checkperms",
+        "script": "checkperms.py"
+      },
+      {
+        "name": "webkit_lint",
+        "script": "webkit_lint.py"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 6808826..eca58f87 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -3795,7 +3795,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_mobile",
@@ -3824,7 +3824,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -3855,7 +3855,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_mobile",
@@ -3884,7 +3884,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -8548,7 +8548,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_mobile",
@@ -8577,7 +8577,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -8608,7 +8608,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_mobile",
@@ -8637,7 +8637,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -11271,7 +11271,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -11301,7 +11301,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -11331,7 +11331,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -13555,7 +13555,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -13585,7 +13585,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -13615,7 +13615,7 @@
           "-v",
           "--upload-results",
           "--browser=android-webview",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
         ],
         "isolate_name": "telemetry_perf_webview_tests",
@@ -17787,7 +17787,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_mobile",
@@ -17816,7 +17816,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -17847,7 +17847,7 @@
           "-v",
           "--upload-results",
           "--browser=android-chromium",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_mobile",
@@ -17876,7 +17876,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -22118,7 +22118,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -22148,7 +22148,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -22180,7 +22180,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -22210,7 +22210,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -26160,7 +26160,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -26190,7 +26190,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -26222,7 +26222,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -26252,7 +26252,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -30186,7 +30186,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -30216,7 +30216,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -30248,7 +30248,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -30278,7 +30278,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -34062,7 +34062,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -34092,7 +34092,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -34124,7 +34124,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -34154,7 +34154,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -37980,7 +37980,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -38010,7 +38010,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -38042,7 +38042,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -38072,7 +38072,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -41940,7 +41940,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -41970,7 +41970,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -42002,7 +42002,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -42032,7 +42032,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -45903,7 +45903,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -45933,7 +45933,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -45965,7 +45965,7 @@
           "-v",
           "--upload-results",
           "--browser=release_x64",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -45995,7 +45995,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -49866,7 +49866,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.common_desktop",
@@ -49896,7 +49896,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
@@ -49928,7 +49928,7 @@
           "-v",
           "--upload-results",
           "--browser=release",
-          "--output-format=chartjson"
+          "--output-format=histograms"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "system_health.memory_desktop",
@@ -49958,7 +49958,7 @@
           "-v",
           "--upload-results",
           "--browser=reference",
-          "--output-format=chartjson",
+          "--output-format=histograms",
           "--max-failures=5",
           "--output-trace-tag=_ref"
         ],
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index eee9b5d..aa830c64 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -26,12 +26,6 @@
 -SysInfoTest.AmountOfMem
 -SysInfoTest.AmountOfTotalDiskSpace
 
-# These tests are affected by an issue with cloning namespace entries from
-# inside a package. See https://crbug.com/826018
--ProcessUtilTest.CloneAlternateDir
--ProcessUtilTest.KillSlowChild
--ProcessUtilTest.SelectivelyClonedDir
-
 # These tests all rely on being able to set the exit code of an externally
 # terminated process, which mx_task_kill() does not support.
 # https://crbug.com/753490.
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 7d695b0..0bef714 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -244,13 +244,22 @@
 -IOThreadEctFieldTrialAndCommandLineBrowserTest.ECTFromCommandLineOverridesFieldTrial
 -IOThreadEctFieldTrialBrowserTest.ForceECTUsingFieldTrial
 
-# No certificate_transparency::TreeStateTracker observes the CTVerifier created
-# by the network service, so Certificate Transparency tests fail.
-# https://crbug.com/803871
--CertificateTransparencyBrowserTest.ProfileRequest
--CertificateTransparencyBrowserTest.SystemRequest
+# SafeBrowsing doesn't yet set up its NetworkContext correctly when the network
+# service is enabled.
+# https://crbug.com/789640
+-SafeBrowsingNetworkContext/NetworkContextConfigurationBrowserTest.DiskCache/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationBrowserTest.DataURL/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationBrowserTest.FileURL/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationBrowserTest.ProxyConfig/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationDataPacBrowserTest.DataPac/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationFilePacBrowserTest.FilePac/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationFtpPacBrowserTest.FtpPac/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationHttpPacBrowserTest.HttpPac/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationHttpsStrippingPacBrowserTest.PacHttpsUrlStripping/0
+-SafeBrowsingNetworkContext/NetworkContextConfigurationProxyOnStartBrowserTest.TestInitialProxyConfig/0
 
-# Fail on Windows only
+# Fails on Windows. It's passing on Linux but this feature needs to be converted
+# to work with network service.
 -ConditionalCacheCountingHelperBrowserTest.Count
 
 # NOTE: if adding an exclusion for an existing failure (e.g. additional test for
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index ed13bb9..988e4dc 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -170,6 +170,26 @@
         'use_swarming': False,
         'os_type': 'android',
       },
+      'android-kitkat-arm-rel': {
+        'additional_compile_targets': [
+          'cronet_test_instrumentation_apk',
+          'monochrome_static_initializers',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_android_gtests',
+          'junit_tests': 'chromium_junit_tests',
+          'isolated_scripts': 'monochrome_apk_checker_isolated_script',
+        },
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'KTU84P',
+              'device_type': 'hammerhead',
+            },
+          ],
+        },
+        'os_type': 'android',
+      },
       'KitKat Phone Tester (dbg)': {
         'test_suites': {
           'gtest_tests': 'chromium_android_gtests',
@@ -1403,6 +1423,24 @@
   {
     'name': 'chromium.linux',
     'machines': {
+      'Cast Audio Linux': {
+        'additional_compile_targets': [
+          'cast_shell',
+          'cast_test_lists',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_cast_audio_gtests',
+        },
+      },
+      'Cast Linux': {
+        'additional_compile_targets': [
+          'cast_shell',
+          'cast_test_lists',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_cast_video_gtests',
+        },
+      },
       'Fuchsia ARM64': {
         'additional_compile_targets': [
           'gn_all',
@@ -1433,11 +1471,6 @@
           'scripts': 'check_network_annotations_script',
         }
       },
-      'linux-jumbo-rel': {
-        'additional_compile_targets': [
-          'all'
-        ]
-      },
       'Linux Builder (dbg)': {
         'additional_compile_targets': [
           'all'
@@ -1449,24 +1482,6 @@
           'sync_integration_tests'
         ]
       },
-      'Cast Audio Linux': {
-        'additional_compile_targets': [
-          'cast_shell',
-          'cast_test_lists',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromium_linux_cast_audio_gtests',
-        },
-      },
-      'Cast Linux': {
-        'additional_compile_targets': [
-          'cast_shell',
-          'cast_test_lists',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromium_linux_cast_video_gtests',
-        },
-      },
       'Fuchsia x64': {
         'additional_compile_targets': [
           'gn_all',
@@ -1506,6 +1521,11 @@
           'empty_main',
         ],
       },
+      'linux-jumbo-rel': {
+        'additional_compile_targets': [
+          'all'
+        ]
+      },
       'linux-ozone-rel': {
         'additional_compile_targets': [
           'chrome',
@@ -1514,6 +1534,23 @@
           'gtest_tests': 'ozone_linux_gtests',
         },
       },
+      'linux-xenial-rel': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-16.04',
+            },
+          ],
+        },
+        'additional_compile_targets': [
+          'all',
+        ],
+        'test_suites': {
+          'gtest_tests': 'chromium_linux_gtests',
+          'isolated_scripts': 'chromium_linux_rel_isolated_scripts',
+          'scripts': 'chromium_linux_scripts',
+        },
+      },
     },
   },
   {
diff --git a/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc b/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
index a3394d8..ce7701c6 100644
--- a/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
@@ -27,7 +27,14 @@
   return source;
 }
 
-extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
+// Explicitly specify some attributes to avoid issues with the linker dead-
+// stripping the following function on macOS, as it is not called directly
+// by fuzz target. LibFuzzer runtime uses dlsym() to resolve that function.
+#if V8_OS_MACOSX
+__attribute__((used)) __attribute__((visibility("default")))
+#endif  // V8_OS_MACOSX
+extern "C" int
+LLVMFuzzerInitialize(int* argc, char*** argv) {
   v8::V8::InitializeICUDefaultLocation((*argv)[0]);
   v8::V8::InitializeExternalStartupData((*argv)[0]);
   v8::V8::SetFlagsFromCommandLine(argc, *argv, true);
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index f5f411e1..9da7d93 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -72,7 +72,12 @@
     'blink_perf.parser',
     'blink_perf.shadow_dom',
     'blink_perf.svg',
-    'memory.top_10_mobile'
+    'memory.top_10_mobile',
+    'system_health.common_desktop',
+    'system_health.common_mobile',
+    'system_health.memory_desktop',
+    'system_health.memory_mobile',
+    'system_health.webview_startup',
 ]
 
 # We currently have two different sharding schemes for android
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 25c65a6..15873a7 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1015,21 +1015,9 @@
             ],
             "experiments": [
                 {
-                    "name": "Brotli",
+                    "name": "NoTransform",
                     "params": {
-                        "exp": "allow_brotli"
-                    }
-                },
-                {
-                    "name": "Control1",
-                    "params": {
-                        "exp": "finch_control1"
-                    }
-                },
-                {
-                    "name": "Control2",
-                    "params": {
-                        "exp": "finch_control2"
+                        "exp": "holdback"
                     }
                 }
             ]
@@ -2306,21 +2294,6 @@
             ]
         }
     ],
-    "OutOfProcessPac": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled"
-                }
-            ]
-        }
-    ],
     "OverflowIconsForMediaControls": [
         {
             "platforms": [
@@ -4000,6 +3973,24 @@
             ]
         }
     ],
+    "V8CacheInlineScriptCode": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "CacheInlineScriptCode",
+                    "enable_features": [
+                        "CacheInlineScriptCode"
+                    ]
+                }
+            ]
+        }
+    ],
     "V8ContextSnapshot": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 8bec42be..c6293ca 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -162,7 +162,6 @@
 crbug.com/591099 css3/flexbox/flex-flow-margins-auto-size.html [ Failure ]
 crbug.com/591099 css3/flexbox/flex-flow-padding.html [ Failure ]
 crbug.com/591099 css3/flexbox/flex-item-contains-strict.html [ Failure ]
-crbug.com/591099 css3/flexbox/flexbox-baseline-margins.html [ Failure ]
 crbug.com/591099 css3/flexbox/flexbox-baseline.html [ Failure ]
 crbug.com/591099 css3/flexbox/flexbox-with-multi-column-property.html [ Failure ]
 crbug.com/591099 css3/flexbox/intrinsic-width-orthogonal-writing-mode.html [ Failure ]
@@ -464,17 +463,9 @@
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/white-space/pre-wrap-002.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-001.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-002.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-003.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-004.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-008.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-009.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-010.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-011.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-012.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-016.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/white-space/seg-break-transformation-017.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-transforms/2d-rotate-js.html [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-transforms/transform-abspos-006.html [ Failure ]
@@ -654,8 +645,6 @@
 crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-vrl-024.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/vertical-alignment-vrl-026.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/wm-propagation-body-006.xht [ Failure ]
-crbug.com/591099 external/wpt/css/cssom-view/elementFromPoint-002.html [ Failure ]
-crbug.com/591099 external/wpt/css/cssom-view/elementFromPoint-003.html [ Failure ]
 crbug.com/591099 external/wpt/css/cssom/interfaces.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/css/geometry/interfaces.worker.html [ Pass ]
 crbug.com/591099 external/wpt/css/selectors/focus-within-004.html [ Pass ]
@@ -839,7 +828,6 @@
 crbug.com/591099 external/wpt/html/dom/documents/dom-tree-accessors/Document.currentScript.html [ Pass ]
 crbug.com/591099 external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/001.html [ Failure Pass ]
 crbug.com/591099 external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/008.html [ Failure Pass Timeout ]
-crbug.com/591099 external/wpt/html/dom/elements/the-innertext-idl-attribute/getter.html [ Failure ]
 crbug.com/591099 external/wpt/html/dom/interfaces.https.html [ Timeout ]
 crbug.com/591099 external/wpt/html/editing/editing-0/autocapitalization/autocapitalize.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html [ Timeout ]
@@ -1047,7 +1035,6 @@
 crbug.com/591099 fast/css-grid-layout/grid-columns-rows-get-set-multiple.html [ Pass Timeout ]
 crbug.com/591099 fast/css-grid-layout/grid-columns-rows-get-set.html [ Timeout ]
 crbug.com/591099 fast/css-grid-layout/grid-item-column-row-get-set.html [ Timeout ]
-crbug.com/591099 fast/css-grid-layout/grid-item-overflow.html [ Failure ]
 crbug.com/591099 fast/css-grid-layout/grid-self-baseline-01.html [ Failure ]
 crbug.com/591099 fast/css-grid-layout/grid-self-baseline-02-b.html [ Failure ]
 crbug.com/591099 fast/css-grid-layout/grid-self-baseline-02.html [ Failure ]
@@ -1220,7 +1207,6 @@
 crbug.com/591099 fast/hidpi/image-set-shape-outside.html [ Failure ]
 crbug.com/591099 fast/history/visited-link-hover-outline-color.html [ Failure ]
 crbug.com/591099 fast/inline-block/baseline-vertical.html [ Failure ]
-crbug.com/591099 fast/inline-block/contenteditable-baseline.html [ Failure ]
 crbug.com/714962 fast/inline-block/tricky-baseline.html [ Failure ]
 crbug.com/591099 fast/inline-block/vertical-align-top-and-bottom-2.html [ Failure ]
 crbug.com/714962 fast/inline/continuation-outlines-with-layers-2.html [ Failure ]
@@ -1264,7 +1250,6 @@
 crbug.com/824918 fast/multicol/dynamic/change-spanner-display.html [ Failure ]
 crbug.com/824918 fast/multicol/flipped-blocks-hit-test.html [ Failure ]
 crbug.com/824918 fast/multicol/hit-test-above-or-below.html [ Failure ]
-crbug.com/824918 fast/multicol/inline-block-baseline.html [ Failure ]
 crbug.com/824918 fast/multicol/nested-one-line-in-inner.html [ Failure ]
 crbug.com/824918 fast/multicol/newmulticol/list-item.html [ Failure ]
 crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-position-on-line-rtl.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
index 82a89943..bea5e441 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -171,83 +171,14 @@
 crbug.com/836897 virtual/threaded/animations/element-animate-positive-delay.html [ Crash ]
 
 # These scrollbar tests should pass.
-crbug.com/836912 compositing/fixed-background-after-style-recalc.html [ Failure ]
-crbug.com/836912 compositing/fixed-body-background-positioned.html [ Failure ]
-crbug.com/836912 compositing/iframe-graphics-tree-changes-parents-does-not.html [ Failure ]
-crbug.com/836912 compositing/layout-width-change.html [ Failure ]
-crbug.com/836912 compositing/low-dpi-non-stacking-context-scrolled-content.html [ Failure ]
-crbug.com/836912 compositing/perspective-interest-rect.html [ Failure ]
-crbug.com/836912 compositing/scroll-with-ancestor-clip.html [ Failure ]
-crbug.com/836912 compositing/culling/filter-occlusion-alpha-large.html [ Failure ]
-crbug.com/836912 compositing/geometry/fixed-in-composited.html [ Failure ]
-crbug.com/836912 compositing/geometry/horizontal-scroll-composited.html [ Failure ]
-crbug.com/836912 compositing/geometry/tall-page-composited.html [ Failure ]
-crbug.com/836912 compositing/iframes/scroll-fixed-transformed-element.html [ Failure ]
-crbug.com/836912 compositing/layer-creation/fixed-position-and-transform.html [ Failure ]
-crbug.com/836912 compositing/layer-creation/fixed-position-scroll.html [ Failure ]
-crbug.com/836912 compositing/layer-creation/fixed-position-under-transform.html [ Failure ]
-crbug.com/836912 compositing/masks/masked-ancestor.html [ Failure ]
-crbug.com/836912 compositing/masks/multiple-masks.html [ Failure ]
-crbug.com/836912 compositing/masks/simple-composited-mask.html [ Failure ]
-crbug.com/836912 compositing/overflow/accelerated-scrolling-with-clip-path.html [ Failure ]
-crbug.com/836912 compositing/overflow/ancestor-overflow-layer-of-sticky-child-of-compositing-container.html [ Failure ]
-crbug.com/836912 compositing/overflow/body-switch-composited-scrolling.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-nested-sticky-deep.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-nested-sticky-left.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-nested-sticky-table.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-nested-sticky-top.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-scroll-background-obscured.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-scroller-can-be-normal-flow.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element-enclosing-layers-stacking-context.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element-enclosing-layers.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element-in-composited-ancestor-with-content-offset.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element-perspective-child-layer.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element-stacking-context.html [ Failure ]
-crbug.com/836912 compositing/overflow/composited-sticky-element.html [ Failure ]
-crbug.com/836912 compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ Failure ]
-crbug.com/836912 compositing/overflow/image-load-overflow-scrollbars.html [ Failure ]
-crbug.com/836912 compositing/overflow/nested-render-surfaces-with-intervening-clip.html [ Failure ]
-crbug.com/836912 compositing/overflow/nested-render-surfaces.html [ Failure ]
-crbug.com/836912 compositing/overflow/nested-scrolling.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-background-fractional-offset.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-background-transparent-to-opaque.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-content-fractional-offset.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-with-local-background-and-child.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-with-local-background.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-with-local-image-background.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll-with-negative-offset-translucent-outline.html [ Failure ]
-crbug.com/836912 compositing/overflow/overflow-scroll.html [ Failure ]
-crbug.com/836912 compositing/overflow/paint-neg-z-order-descendants-into-scrolling-contents-layer.html [ Failure ]
-crbug.com/836912 compositing/overflow/reparented-overlay-scrollbars-should-respect-ancestor-clip.html [ Failure ]
-crbug.com/836912 compositing/overflow/reparented-unclipped-overlay-scrollbars-with-offset-from-renderer.html [ Failure ]
-crbug.com/836912 compositing/overflow/scaled-overflow.html [ Failure ]
-crbug.com/836912 compositing/overflow/scroll-children-do-not-paint-comp-scroll-phase.html [ Failure ]
-crbug.com/836912 compositing/overflow/scroll-parent-with-non-stacking-context-composited-ancestor.html [ Failure ]
-crbug.com/836912 compositing/overflow/scrollbar-layer-placement.html [ Failure ]
-crbug.com/836912 compositing/overflow/tiled-mask.html [ Failure ]
-crbug.com/836912 compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
-crbug.com/836912 compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers.html [ Failure ]
-crbug.com/836912 compositing/overlap-blending/children-opacity-huge.html [ Failure ]
-crbug.com/836912 compositing/rtl/rtl-absolute-overflow-scrolled.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-absolute-overflow.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-and-writing-mode-scrolling.html [ Failure ]
-crbug.com/836912 compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-fixed-overflow.html [ Failure ]
-crbug.com/836912 compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-iframe-absolute-overflow.html [ Failure ]
-crbug.com/836912 compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-iframe-fixed-overflow.html [ Failure ]
 crbug.com/836912 compositing/rtl/rtl-overflow-invalidation.html [ Failure ]
-crbug.com/836912 compositing/rtl/rtl-overflow-scrolling.html [ Failure ]
 crbug.com/836912 compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
-crbug.com/836912 compositing/squashing/attempting-to-squash-into-compositing-container.html [ Failure ]
-crbug.com/836912 compositing/squashing/attempting-to-squash-into-stacking-ancestor.html [ Failure ]
-crbug.com/836912 compositing/squashing/frame-clip-squashed-scrolled.html [ Failure ]
-crbug.com/836912 compositing/squashing/squashing-does-not-stop-transform-propagation.html [ Failure ]
-crbug.com/836912 compositing/squashing/squashing-inside-perspective-with-reparented-scrolling.html [ Failure ]
-crbug.com/836912 compositing/squashing/universal-accelerated-overflow-scrolling.html [ Failure ]
 crbug.com/836912 compositing/squashing/vertical-writing-mode-squashed.html [ Failure ]
-crbug.com/836912 scrollbars/border-box-rect-clips-scrollbars.html [ Failure ]
 crbug.com/836912 scrollbars/scrollbar-added-during-drag.html [ Crash ]
 crbug.com/836912 scrollbars/scrollbar-removed-by-viewport-crash.html [ Crash ]
 
@@ -285,60 +216,33 @@
 crbug.com/840017 compositing/overflow/overflow-scaled-descendant-overlapping.html [ Crash ]
 crbug.com/840017 compositing/scaling/tiled-layer-recursion.html [ Crash ]
 
-# Lack of content layer support (other than scrollbar layers)
-crbug.com/840037 compositing/canvas-with-object-fit-contain-in-composited-layer.html [ Failure ]
-crbug.com/840037 compositing/culling/tile-occlusion-boundaries.html [ Failure ]
-crbug.com/840037 compositing/direct-image-compositing.html [ Failure ]
-crbug.com/840037 compositing/img-layer-grow.html [ Failure ]
-crbug.com/840037 compositing/layers-inside-overflow-scroll.html [ Failure ]
-crbug.com/840037 compositing/lots-of-img-layers-with-opacity.html [ Failure ]
-crbug.com/840037 compositing/lots-of-img-layers.html [ Failure ]
-crbug.com/840037 compositing/nested-direct-image-compositing.html [ Failure ]
-crbug.com/840037 compositing/self-painting-layers.html [ Failure ]
-crbug.com/840037 compositing/video-frame-size-change.html [ Failure ]
-crbug.com/840037 compositing/backface-visibility/backface-visibility-image.html [ Failure ]
-crbug.com/840037 compositing/backface-visibility/backface-visibility-webgl.html [ Failure ]
-crbug.com/840037 compositing/color-matching/image-color-matching.html [ Failure ]
-crbug.com/840037 compositing/draws-content/canvas-simple-background.html [ Failure ]
-crbug.com/840037 compositing/draws-content/webgl-simple-background.html [ Failure ]
-crbug.com/840037 compositing/geometry/video-fixed-scrolling.html [ Failure ]
-crbug.com/840037 compositing/geometry/video-opacity-overlay.html [ Failure ]
-crbug.com/840037 compositing/images/content-image-change.html [ Failure ]
-crbug.com/840037 compositing/masks/direct-image-mask.html [ Failure ]
-crbug.com/840037 compositing/masks/mask-with-removed-filters.html [ Failure ]
-crbug.com/840037 compositing/overflow/overflow-compositing-descendant.html [ Failure ]
-crbug.com/840037 compositing/overflow/overflow-hidden-canvas-layer.html [ Failure ]
-crbug.com/840037 compositing/overflow/scroll-ancestor-update.html [ Failure ]
-crbug.com/840037 compositing/plugins/webplugin-alpha.html [ Failure ]
-crbug.com/840037 compositing/plugins/webplugin-no-alpha.html [ Failure ]
-crbug.com/840037 compositing/plugins/webplugin-reflection.html [ Failure ]
-crbug.com/840037 compositing/reflections/load-video-in-reflection.html [ Failure ]
-crbug.com/840037 compositing/video/video-reflection.html [ Failure ]
-crbug.com/840037 compositing/visibility/visibility-image-layers.html [ Failure ]
-crbug.com/840037 compositing/visibility/visibility-simple-canvas2d-layer.html [ Failure ]
-crbug.com/840037 compositing/visibility/visibility-simple-video-layer.html [ Failure ]
-crbug.com/840037 compositing/visibility/visibility-simple-webgl-layer.html [ Failure ]
-crbug.com/840037 compositing/webgl/webgl-no-alpha.html [ Failure ]
-crbug.com/840037 compositing/webgl/webgl-nonpremultiplied-blend.html [ Failure ]
-crbug.com/840037 compositing/webgl/webgl-reflection.html [ Failure ]
-crbug.com/840037 compositing/webgl/webgl-repaint.html [ Failure ]
-crbug.com/840037 compositing/webgl/webgl-with-accelerated-background-color.html [ Failure ]
-
 # Reflection drawn at wrong position.
 crbug.com/840038 compositing/reflections/ [ Skip ]
 crbug.com/840038 compositing/overflow/reflected-overlay-scrollbars-should-appear-without-compositing.html [ Failure ]
 crbug.com/840038 compositing/overlap-blending/reflection-opacity-huge.html [ Failure ]
+crbug.com/840038 compositing/plugins/webplugin-reflection.html [ Failure ]
 crbug.com/840038 compositing/squashing/squashing-reflection-disallowed.html [ Failure ]
+crbug.com/840038 compositing/video/video-reflection.html [ Failure ]
+crbug.com/840038 compositing/webgl/webgl-reflection.html [ Failure ]
 
 # Rounding differences.
 # Maybe due to different render surface generation? (Needs investigation.)
 Bug(none) compositing/backface-visibility-transformed.html [ Failure ]
+Bug(none) compositing/color-matching/image-color-matching.html [ Failure ]
 Bug(none) compositing/flat-with-transformed-child.html [ Failure ]
+Bug(none) compositing/lots-of-img-layers-with-opacity.html [ Failure ]
+Bug(none) compositing/lots-of-img-layers.html [ Failure ]
 Bug(none) compositing/opacity-with-mask.html [ Failure ]
 Bug(none) compositing/perpendicular-layer-sorting.html [ Failure ]
+Bug(none) compositing/perspective-interest-rect.html [ Failure ]
 Bug(none) compositing/geometry/layer-due-to-layer-children.html [ Failure ]
-Bug(none) compositing/overflow/reflected-overlay-scrollbars-should-respect-ancestor-clip.html [ Failure ]
 Bug(none) compositing/shadows/shadow-drawing.html [ Failure ]
+Bug(none) compositing/masks/direct-image-mask.html [ Failure ]
+Bug(none) compositing/masks/masked-ancestor.html [ Failure ]
+Bug(none) compositing/masks/multiple-masks.html [ Failure ]
+Bug(none) compositing/masks/simple-composited-mask.html [ Failure ]
+Bug(none) compositing/overflow/scaled-overflow.html [ Failure ]
+Bug(none) compositing/overflow/tiled-mask.html [ Failure ]
 
 # Failures that don't belong to any of above categories. Investigation needed.
 Bug(none) compositing/overflow/nested-render-surfaces-with-rotation.html [ Failure ]
@@ -346,8 +250,10 @@
 Bug(none) compositing/overflow-trumps-transform-style.html [ Failure ]
 Bug(none) compositing/backface-visibility/backface-visibility-hierarchical-transform.html [ Failure ]
 Bug(none) compositing/geometry/clipping-foreground.html [ Failure ]
+Bug(none) compositing/masks/mask-with-removed-filters.html [ Failure ]
 Bug(none) compositing/video/video-controls-layer-creation.html [ Failure ]
 Bug(none) compositing/visibility/overlays.html [ Failure ]
+Bug(none) compositing/visibility/visibility-image-layers.html [ Failure ]
 
 # These tests should also pass. TODO(pdr): Enable these tests.
 Bug(none) virtual/scroll_customization/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 9681b33..010bc3b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2774,9 +2774,6 @@
 crbug.com/803200 external/wpt/websockets/opening-handshake/005.html?wss [ Pass Failure ]
 crbug.com/803200 virtual/off-main-thread-websocket/external/wpt/websockets/cookies/006.html?wss [ Failure ]
 crbug.com/803200 virtual/off-main-thread-websocket/external/wpt/websockets/opening-handshake/005.html?wss [ Pass Failure ]
-# These tests are failing because of experimental off-main-thread WebSocket
-# implementation.
-crbug.com/831320 virtual/off-main-thread-websocket/http/tests/websocket/close-code-and-reason.html [ Pass Failure ]
 
 crbug.com/805463 external/wpt/acid/acid3/numbered-tests.html [ Skip ]
 
@@ -2789,6 +2786,7 @@
 crbug.com/832071 virtual/navigation-mojo-response/external/wpt/service-workers/service-worker/worker-client-id.https.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Retina ] external/wpt/pointerevents/pointerevent_touch-action-button-test_touch-manual.html [ Skip ]
 crbug.com/626703 [ Retina ] external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html [ Skip ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/pointerevents/pointerevent_touch-action-button-test_touch-manual.html [ Skip ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html [ Skip ]
@@ -4633,6 +4631,9 @@
 
 crbug.com/832274 [ Linux ] fast/forms/search/search-appearance-basic.html [ Pass Failure ]
 
+crbug.com/840881 external/wpt/service-workers/service-worker/resource-timing.https.html [ Failure ]
+crbug.com/840881 virtual/outofblink-cors/external/wpt/service-workers/service-worker/resource-timing.https.html [ Failure ]
+
 # Sheriff 2018-04-12
 crbug.com/831993 [ Linux ] virtual/gpu-rasterization/images/cross-fade-overflow-position.html [ Pass Timeout ]
 
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 90e9d3b..338c800 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -680,17 +680,17 @@
   {
     "prefix": "off-main-thread-websocket",
     "base": "external/wpt/websockets",
-    "args": ["--enable-features=OffMainThreadWebSocket"]
+    "args": ["--disable-features=OffMainThreadWebSocket"]
   },
   {
     "prefix": "off-main-thread-websocket",
     "base": "http/tests/websocket",
-    "args": ["--enable-features=OffMainThreadWebSocket"]
+    "args": ["--disable-features=OffMainThreadWebSocket"]
   },
   {
     "prefix": "off-main-thread-websocket",
     "base": "http/tests/security/mixedContent/websocket",
-    "args": ["--enable-features=OffMainThreadWebSocket"]
+    "args": ["--disable-features=OffMainThreadWebSocket"]
   },
   {
     "prefix": "webrtc-wpt-unified-plan",
@@ -702,5 +702,10 @@
     "base": "media",
     "args": ["--enable-features=UseSurfaceLayerForVideo",
              "--enable-display-compositor-pixel-dump"]
+  },
+  {
+    "prefix": "user-activation-v2",
+    "base": "user-activation-v2",
+    "args": ["--enable-features=UserActivationV2"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index c517945..46632e3 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -139997,6 +139997,16 @@
      {}
     ]
    ],
+   "fetch/corb/resources/html-js-polyglot2.js": [
+    [
+     {}
+    ]
+   ],
+   "fetch/corb/resources/html-js-polyglot2.js.headers": [
+    [
+     {}
+    ]
+   ],
    "fetch/corb/resources/js-mislabeled-as-html-nosniff.js": [
     [
      {}
@@ -140742,6 +140752,11 @@
      {}
     ]
    ],
+   "generic-sensor/README.md": [
+    [
+     {}
+    ]
+   ],
    "generic-sensor/generic-sensor-feature-policy-test.sub.js": [
     [
      {}
@@ -156357,6 +156372,11 @@
      {}
     ]
    ],
+   "orientation-sensor/orientation-sensor-tests.js": [
+    [
+     {}
+    ]
+   ],
    "page-visibility/OWNERS": [
     [
      {}
@@ -160827,6 +160847,16 @@
      {}
     ]
    ],
+   "server-timing/service_worker_idl-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "server-timing/sw.js": [
+    [
+     {}
+    ]
+   ],
    "server-timing/test_server_timing.html.sub.headers": [
     [
      {}
@@ -222413,9 +222443,9 @@
      {}
     ]
    ],
-   "orientation-sensor/OrientationSensor.https.html": [
+   "orientation-sensor/AbsoluteOrientationSensor.https.html": [
     [
-     "/orientation-sensor/OrientationSensor.https.html",
+     "/orientation-sensor/AbsoluteOrientationSensor.https.html",
      {}
     ]
    ],
@@ -222455,6 +222485,12 @@
      {}
     ]
    ],
+   "orientation-sensor/RelativeOrientationSensor.https.html": [
+    [
+     "/orientation-sensor/RelativeOrientationSensor.https.html",
+     {}
+    ]
+   ],
    "orientation-sensor/idlharness.https.html": [
     [
      "/orientation-sensor/idlharness.https.html",
@@ -232091,6 +232127,12 @@
      {}
     ]
    ],
+   "server-timing/service_worker_idl.html": [
+    [
+     "/server-timing/service_worker_idl.html",
+     {}
+    ]
+   ],
    "server-timing/test_server_timing.html": [
     [
      "/server-timing/test_server_timing.html",
@@ -237103,6 +237145,24 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html": [
+    [
+     "/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html",
+     {}
+    ]
+   ],
+   "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html": [
+    [
+     "/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html",
+     {}
+    ]
+   ],
+   "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html": [
+    [
+     "/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html": [
     [
      "/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html",
@@ -237169,6 +237229,24 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html": [
+    [
+     "/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html",
+     {}
+    ]
+   ],
+   "webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html": [
+    [
+     "/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html",
+     {}
+    ]
+   ],
+   "webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html": [
+    [
+     "/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-audiodestinationnode-interface/idl-test.html": [
     [
      "/webaudio/the-audio-api/the-audiodestinationnode-interface/idl-test.html",
@@ -244819,6 +244897,16 @@
      {}
     ]
    ],
+   "xhr/sync-no-progress.any.js": [
+    [
+     "/xhr/sync-no-progress.any.html",
+     {}
+    ],
+    [
+     "/xhr/sync-no-progress.any.worker.html",
+     {}
+    ]
+   ],
    "xhr/template-element.html": [
     [
      "/xhr/template-element.html",
@@ -250561,11 +250649,11 @@
    "support"
   ],
   "./README.md": [
-   "2ad80e88dfd9bd3ce7cf4d1b9e53b7d61c104881",
+   "766bb4d66deef9bcb90339d76e3280abb36103df",
    "support"
   ],
   "./lint.whitelist": [
-   "64e7ead5a7fa09f59be4dac4ccb5a1e7bf3cb4fc",
+   "b740ea6ee933d028f6b2bef92cc473778b76002f",
    "support"
   ],
   "./update-built-tests.sh": [
@@ -257265,11 +257353,11 @@
    "testharness"
   ],
   "accelerometer/Accelerometer-disabled-by-feature-policy.https-expected.txt": [
-   "52f629ee03b150890f9177af60ab82f716e16ae3",
+   "2f3a532fcced3d1f18bc59a8416a3f5b1ed138cf",
    "support"
   ],
   "accelerometer/Accelerometer-disabled-by-feature-policy.https.html": [
-   "96e5a86bf78239f3aa4a79b32bb4308de0d5f60e",
+   "efa020e5d2384a3b753d41d32fd9e62337b5fd9e",
    "testharness"
   ],
   "accelerometer/Accelerometer-disabled-by-feature-policy.https.html.headers": [
@@ -257277,27 +257365,27 @@
    "support"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt": [
-   "a96695ef8430701cb0c45ffaa7710d1f96f90125",
+   "9e402c8857b93ef3f7556a5179851042a2204bc9",
    "support"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html": [
-   "f20f89a31daf399a7946b928e8deca9651d38487",
+   "0e27e5bad61367dcc348aef1ac813e70a2e2aa36",
    "testharness"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https-expected.txt": [
-   "dcbeb063b25e2c3f01bc7b68491f726b25c1634c",
+   "764f3410ef5b7fbfc70292abc51427cc94517d29",
    "support"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https.html": [
-   "7ce8c39f0af6c932d1e6b6f75843c090f7274b80",
+   "5e072aa513efc16946b2f93ecb815d45dd6cadd2",
    "testharness"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy.https-expected.txt": [
-   "874b4f9b7ed78e090154e92f1cd9afa2a3af3461",
+   "b031e85d10ace03f19294940e92f1f6837623f76",
    "support"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy.https.html": [
-   "dbda7cfa838c1f2d4296b545034d42f0064f331a",
+   "59207b5dbc249addf4a9c0c5886e1486a2d6eb94",
    "testharness"
   ],
   "accelerometer/Accelerometer-enabled-by-feature-policy.https.html.headers": [
@@ -257305,11 +257393,11 @@
    "support"
   ],
   "accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https-expected.txt": [
-   "d4f6158e03f73ef30f855ff407de2b73e564df81",
+   "126c72a76cdd9c2b318e0cb1b806417100d1e6a3",
    "support"
   ],
   "accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html": [
-   "b8fb07cdd23dce9690de719387b9d505382772f5",
+   "fe9960de7b6ac203a36096f90ede31131e38cb1b",
    "testharness"
   ],
   "accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html.headers": [
@@ -257317,11 +257405,11 @@
    "support"
   ],
   "accelerometer/Accelerometer.https-expected.txt": [
-   "0be3c1ea85e316926dbd226fbdb4244fc94eab07",
+   "eb8f690a2264e1f0a9171453b0d343a701ecd0a1",
    "support"
   ],
   "accelerometer/Accelerometer.https.html": [
-   "3ff5f61b25c52142e9796f3903d7f2bcaa30314c",
+   "bef928057d49b59c2ede3916e49abdd730dc9582",
    "testharness"
   ],
   "accelerometer/Accelerometer_insecure_context.html": [
@@ -257329,11 +257417,11 @@
    "testharness"
   ],
   "accelerometer/Accelerometer_onerror-manual.https-expected.txt": [
-   "31feaa72d7b84d017d459344462e0a38a72632ba",
+   "5655b4361914f873488e5a79f45f0fee1e3cf5d1",
    "support"
   ],
   "accelerometer/Accelerometer_onerror-manual.https.html": [
-   "c82f9595dc2582b2da40549a358da1c3fc2ff820",
+   "f6a624b70b401c0802c5f189c89e85d63fe58795",
    "manual"
   ],
   "accelerometer/LinearAccelerationSensor-shake-threshold-manual.https-expected.txt": [
@@ -336585,13 +336673,21 @@
    "support"
   ],
   "fetch/corb/resources/html-js-polyglot.js": [
-   "7fc30035583764941078fb53f950c52a217d6893",
+   "529e81f0fa614cb1707cf759f6bf1c7990f4d51f",
    "support"
   ],
   "fetch/corb/resources/html-js-polyglot.js.headers": [
    "41e260e7df49e0e4ddb1fc5df11913dbda15edd7",
    "support"
   ],
+  "fetch/corb/resources/html-js-polyglot2.js": [
+   "2a314fc6b634c7f6a6117d5602f9a4520397850d",
+   "support"
+  ],
+  "fetch/corb/resources/html-js-polyglot2.js.headers": [
+   "41e260e7df49e0e4ddb1fc5df11913dbda15edd7",
+   "support"
+  ],
   "fetch/corb/resources/js-mislabeled-as-html-nosniff.js": [
    "ec322736e35e0649e1f3cd4d5b88e2f211436e2b",
    "support"
@@ -336641,7 +336737,7 @@
    "testharness"
   ],
   "fetch/corb/script-html-js-polyglot.sub.html": [
-   "4b28bd95bd19ea95df3e78bbfc477fb7dea60e29",
+   "0e9042bc4d7492b17aeb69c9062ca9682000936b",
    "testharness"
   ],
   "fetch/corb/script-html-via-cross-origin-blob-url.sub.html": [
@@ -337516,16 +337612,20 @@
    "1cb8a0e23d31dcdeb5ba273a40e35c021c0c53f2",
    "support"
   ],
+  "generic-sensor/README.md": [
+   "48615c1b9e70e02c02ea0246eb7ca1953bab0c68",
+   "support"
+  ],
   "generic-sensor/SensorErrorEvent-constructor.https.html": [
    "99bcfb42c91e084a3b847ab4bab2bad80e548540",
    "testharness"
   ],
   "generic-sensor/generic-sensor-feature-policy-test.sub.js": [
-   "c7c9c4d1c578f267cbb4241d7ea7a981be6f49ee",
+   "f90a08bd96c729c5c166fd628e69f01c3413a78c",
    "support"
   ],
   "generic-sensor/generic-sensor-tests.js": [
-   "6364f1838215eaafd91b86690039da41d4c19cd1",
+   "a23c776f7536e3f406c78a5aa02359c7dde8c745",
    "support"
   ],
   "generic-sensor/idlharness.https.html": [
@@ -337737,7 +337837,7 @@
    "manual"
   ],
   "gyroscope/Gyroscope-disabled-by-feature-policy.https.html": [
-   "bc6f85cb3506d3e3e062eef43dca88bf3a4ebe89",
+   "64db7d9fe9726d6c04e97802f2328e3cfe6e2585",
    "testharness"
   ],
   "gyroscope/Gyroscope-disabled-by-feature-policy.https.html.headers": [
@@ -337745,15 +337845,15 @@
    "support"
   ],
   "gyroscope/Gyroscope-enabled-by-feature-policy-attribute-redirect-on-load.https.html": [
-   "cc1f4082fa420a9076f929d88268f5a1d921a44a",
+   "6f9c2974d9fa82d85ecc299990295f7bb44fbced",
    "testharness"
   ],
   "gyroscope/Gyroscope-enabled-by-feature-policy-attribute.https.html": [
-   "75429c87bbebd19df7b4edaddc5869c6202b3fab",
+   "413cad8e563dd38decb5e388a3a10363417cd413",
    "testharness"
   ],
   "gyroscope/Gyroscope-enabled-by-feature-policy.https.html": [
-   "5129c05f9b226650a9b7fed4841871045525bf15",
+   "5c723e19f2f67d50d7e5d1ea75a5c09b4eeec4a3",
    "testharness"
   ],
   "gyroscope/Gyroscope-enabled-by-feature-policy.https.html.headers": [
@@ -337761,7 +337861,7 @@
    "support"
   ],
   "gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html": [
-   "2603bd858929ae6f84ebbe0cce34c5417ccdae06",
+   "3b6cc272c04aee446ce8dffbe041330e5c2298ea",
    "testharness"
   ],
   "gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html.headers": [
@@ -337769,11 +337869,11 @@
    "support"
   ],
   "gyroscope/Gyroscope.https-expected.txt": [
-   "d2e562f7390f727627d5c39053c5c99313508428",
+   "b0a057a754e19f96e120240cdb478f58f12240ab",
    "support"
   ],
   "gyroscope/Gyroscope.https.html": [
-   "504abfa42529e08576e49c3296464bcea5fe0b8a",
+   "e2b1919195d0af74bb7733a6d8a299adf43f4ee4",
    "testharness"
   ],
   "gyroscope/Gyroscope_insecure_context.html": [
@@ -337781,7 +337881,7 @@
    "testharness"
   ],
   "gyroscope/Gyroscope_onerror-manual.https.html": [
-   "1e15b883bd317ca83783864fc563794cb0f6df8e",
+   "c8f346f00bbc296e89e132926f974ee408d66fcf",
    "manual"
   ],
   "gyroscope/OWNERS": [
@@ -339901,7 +340001,7 @@
    "support"
   ],
   "html/browsers/origin/origin-of-data-document.html": [
-   "9fec457691ac4b071e9bc8de1ebf6f13dbadd4e5",
+   "844b162882b27fd3ec0a07c8d4c1a6a6254b69ca",
    "testharness"
   ],
   "html/browsers/origin/relaxing-the-same-origin-restriction/.gitkeep": [
@@ -358373,11 +358473,11 @@
    "support"
   ],
   "magnetometer/Magnetometer-disabled-by-feature-policy.https-expected.txt": [
-   "9b71c24ebac69e42bc88eb682125608f2d121482",
+   "51726aed6ede1a2cad40d77b3d10c05017d34b6e",
    "support"
   ],
   "magnetometer/Magnetometer-disabled-by-feature-policy.https.html": [
-   "9af542095f2fcd57691c0432ef3248b04a0207a0",
+   "33e6cbd89ec34e729d84f5a50bd7f75c47fa42e8",
    "testharness"
   ],
   "magnetometer/Magnetometer-disabled-by-feature-policy.https.html.headers": [
@@ -358385,27 +358485,27 @@
    "support"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt": [
-   "eeb13f720102302f7431eda65b7582e9ae269dbe",
+   "a20ddc097f247a109ee8c4d4322de2586844e439",
    "support"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html": [
-   "47829ff5747eed99ba22e79b12ddfff288fd031e",
+   "3686719c3eab1867e5f5755dea11734f52123a68",
    "testharness"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https-expected.txt": [
-   "ebd246e2346528a6a79886a8a8cce2b4b8eca945",
+   "429256377651387a4f60351f75375052816c6e69",
    "support"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https.html": [
-   "3b6314e9176a24976d9d882644c30f00554eed6d",
+   "8c61e61e1a7b43a080991d12c7b469e30613a165",
    "testharness"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy.https-expected.txt": [
-   "c23861af9d1d4a8eaad2a3bf21bd4603d9eff5b2",
+   "7ea149699a3c4a0ceedd124e91e8bad80dd5dbff",
    "support"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy.https.html": [
-   "05128cdb7171ba230143e7b68b09968a484b602a",
+   "806b736a6320c82d5c6f10fae30ba5c35e695e10",
    "testharness"
   ],
   "magnetometer/Magnetometer-enabled-by-feature-policy.https.html.headers": [
@@ -358413,11 +358513,11 @@
    "support"
   ],
   "magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https-expected.txt": [
-   "896aaed62339097d41937a839a8f3114b4d54611",
+   "5d15cb4f2b65da21c7115d614a552b4d0ca05599",
    "support"
   ],
   "magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html": [
-   "3240dafd2bc810dea0dc1ebc31728c86a29f2ec5",
+   "5871a3396ac4030f2202ea27f48fcb715de26236",
    "testharness"
   ],
   "magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html.headers": [
@@ -358425,11 +358525,11 @@
    "support"
   ],
   "magnetometer/Magnetometer.https-expected.txt": [
-   "e782e6fc41a3716f87073b5aa0dcac10690e4e03",
+   "19d0c8dddeee0d869e7924c71761565fc8ea34d0",
    "support"
   ],
   "magnetometer/Magnetometer.https.html": [
-   "240e7d0af55b8681f2f45ca22283634acc406325",
+   "85b033df53fcaec4a55175f6a9438bd2abf5e6ff",
    "testharness"
   ],
   "magnetometer/Magnetometer_insecure_context.html": [
@@ -358437,11 +358537,11 @@
    "testharness"
   ],
   "magnetometer/Magnetometer_onerror-manual.https-expected.txt": [
-   "5535f83d0f9d1dc22d9d27d2dd000cd076e6dd98",
+   "71d027277b0af0e5264cc3f520145c0bdac528b6",
    "support"
   ],
   "magnetometer/Magnetometer_onerror-manual.https.html": [
-   "da4e6b8975beecdcae24da26920a56a652f781e4",
+   "5da81d90bd6960c94d5b3dd7592aa4c93d996e1a",
    "manual"
   ],
   "magnetometer/OWNERS": [
@@ -367265,7 +367365,7 @@
    "manual"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html": [
-   "0c7657e32b46c64e5f050f04e40ccfc0dce32e47",
+   "d0ad686df1fc6e176a23f827f5afb61c55a7754b",
    "testharness"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html.headers": [
@@ -367273,15 +367373,15 @@
    "support"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html": [
-   "71a6966fb91f8ddfe15ee690c843e4621a3b4720",
+   "c666593365dba60ccf03a354f997bb86cab6581c",
    "testharness"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute.https.html": [
-   "9bbe39e1be4a669f31f0fb3960aca22d7d159c5e",
+   "cd0c5fb506d123dea5e162994e68e908b8e18fa0",
    "testharness"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html": [
-   "9f110b8ceca3dd3ce3d2a9e2567d92a3fc6462bf",
+   "260958b236c47b8d8796c8003ce3c1b1cedbe162",
    "testharness"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html.headers": [
@@ -367289,7 +367389,7 @@
    "support"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html": [
-   "c4404cf0b16c4d9ad15308392ecf52caee445b86",
+   "7d7709582f6fcfadd7000b79114a36adbf1c7dde",
    "testharness"
   ],
   "orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html.headers": [
@@ -367300,6 +367400,10 @@
    "e642947463a634fa4bc4e3adbfe85bc89b3ca174",
    "support"
   ],
+  "orientation-sensor/AbsoluteOrientationSensor.https.html": [
+   "c482c21d4336decf5d4c47b4e6911dc2b03a19e9",
+   "testharness"
+  ],
   "orientation-sensor/OWNERS": [
    "3f4a18cda77fd2fcb47f8892192c691cf201c574",
    "support"
@@ -367308,20 +367412,16 @@
    "b6295d76af50ccfbb086921827dc7e1eeae3e26b",
    "support"
   ],
-  "orientation-sensor/OrientationSensor.https.html": [
-   "ea9a61f9d526183071a0336e11ca844f59c284a9",
-   "testharness"
-  ],
   "orientation-sensor/OrientationSensor_insecure_context.html": [
    "eb64da888f1bedf9d5ed8b11edc1626da88b322b",
    "testharness"
   ],
   "orientation-sensor/OrientationSensor_onerror-manual.https.html": [
-   "6f0eb976affc21e49f48c42c1bd9d9eb0083ee40",
+   "c7d57da56142057f23503da971875df46ef6d137",
    "manual"
   ],
   "orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html": [
-   "8c6c5f8f3bc4421f5143a3e4fab287564cf4907d",
+   "c45d0a4cbfa68431c97e6d6288a9e57161100733",
    "testharness"
   ],
   "orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html.headers": [
@@ -367329,15 +367429,15 @@
    "support"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html": [
-   "34a79c9033a41c0aceab3fa6a470dd5a76f2ac81",
+   "fb57e3d552c56f9b7e21f461b4938d850c5c9d8a",
    "testharness"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute.https.html": [
-   "0e6260ccdaa8163b8db96516960be226a4d85ba7",
+   "04bf99c095016b4f39a99819f0cf3b5742c7e436",
    "testharness"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html": [
-   "243b2d60d4c528a77e9cfb68fa256b35234e7346",
+   "2286335655c89a9428193cd42a42ec1df7ddf333",
    "testharness"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html.headers": [
@@ -367345,7 +367445,7 @@
    "support"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html": [
-   "c9f234f6508fb778fdf91277bb8950c99479979d",
+   "4b529302141beb7cdf2b6569a0f836dc378ba062",
    "testharness"
   ],
   "orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html.headers": [
@@ -367356,10 +367456,18 @@
    "bfda2f7ce33918474e05c50052b5216b254c6226",
    "support"
   ],
+  "orientation-sensor/RelativeOrientationSensor.https.html": [
+   "9bf83c64eb5dcf284eb6c45db8ac278ab3c637a2",
+   "testharness"
+  ],
   "orientation-sensor/idlharness.https.html": [
    "1f94329cb330a1a904d409a6e158a5fe9f8dd709",
    "testharness"
   ],
+  "orientation-sensor/orientation-sensor-tests.js": [
+   "250d6009213e72069acd56e847fc9509c52c1d90",
+   "support"
+  ],
   "page-visibility/OWNERS": [
    "ef8e006eb532afaf6f561aa9ba2a46afa6b5693c",
    "support"
@@ -367785,7 +367893,7 @@
    "testharness"
   ],
   "payment-request/payment-request-not-exposed.https.worker.js": [
-   "b1c3cdc182cb967ec11b1cad826b333511203d77",
+   "957e88d8e5f212254140487b6980abf40cf5ed96",
    "testharness"
   ],
   "payment-request/payment-request-onshippingaddresschange-attribute.https.html": [
@@ -377808,6 +377916,18 @@
    "10f756bbf749b7ad8f7c6eb4efe752ee79c44b4a",
    "testharness"
   ],
+  "server-timing/service_worker_idl-expected.txt": [
+   "ecc444813caba5ddc5baece8fdbaa6ca377a6d10",
+   "support"
+  ],
+  "server-timing/service_worker_idl.html": [
+   "cb5ea3136399f88fb6c4e8071ad8e3b7ccebb242",
+   "testharness"
+  ],
+  "server-timing/sw.js": [
+   "0c12328f152814e2f0bde7fe026cf12c8ea77ff0",
+   "support"
+  ],
   "server-timing/test_server_timing.html": [
    "7c778ca856e5cff0bbc785f59c9ccf1ec86456fb",
    "testharness"
@@ -379785,7 +379905,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/resource-timing-worker.js": [
-   "2a47775874086c1cdff257e3243af1af0b5e84be",
+   "04a965dc0324c035bb3331e546d9bb43230d5eac",
    "support"
   ],
   "service-workers/service-worker/resources/respond-then-throw-worker.js": [
@@ -383653,7 +383773,7 @@
    "support"
   ],
   "url/setters_tests.json": [
-   "8d58bd1c98f4d987e6abe66659c812591b197e60",
+   "7c9707c02714de9a17989ecc71775978e994ae8a",
    "support"
   ],
   "url/toascii.json": [
@@ -384956,6 +385076,18 @@
    "7e35ac29f00d39c84230535212c0b9ea081951d3",
    "testharness"
   ],
+  "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html": [
+   "a4058a967414b087e19ad8dd2af0999152f1491c",
+   "testharness"
+  ],
+  "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html": [
+   "f4a4122ebd500cba1d5fb383369849e31f73d7f7",
+   "testharness"
+  ],
+  "webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html": [
+   "9389a7e0b4c115a756e547225140150136d1103d",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html": [
    "e2320e33ef1df0155d5fcf536550e0e398b15407",
    "testharness"
@@ -385012,6 +385144,18 @@
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
+  "webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html": [
+   "03d32deacab7a98f3cce29562b84158e3f512668",
+   "testharness"
+  ],
+  "webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html": [
+   "0ef6d19b75e1437ea71846d5a0d1af7dd426dc83",
+   "testharness"
+  ],
+  "webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html": [
+   "21ea30d8fa92e908da66e79fd127e8fa0203a4c5",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
@@ -386329,7 +386473,7 @@
    "support"
   ],
   "webrtc/RTCDTMFSender-insertDTMF.https.html": [
-   "79574cff7e0500cecaf7e3ae182e89d98f49ce72",
+   "a5fc15d44fe572744c07d933b9204d9319c968cd",
    "testharness"
   ],
   "webrtc/RTCDTMFSender-ontonechange-long.https.html": [
@@ -386389,7 +386533,7 @@
    "support"
   ],
   "webrtc/RTCIceTransport.html": [
-   "c145a8a34c79dd970475e77ff5bf1a363c0ac56c",
+   "db758cc2a744c049c291575e408dbb5f280cdf19",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addIceCandidate-expected.txt": [
@@ -386421,7 +386565,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-canTrickleIceCandidates.html": [
-   "0f585a89bd8f25aa8f83b6ec39b704cbb8e970b2",
+   "8401fdc22f8f8867aa361f6a83834cdeb7a2a9d1",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-connectionState-expected.txt": [
@@ -386501,7 +386645,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-getStats.https.html": [
-   "9446d7bc1aefa7edd28b425415d983d69311e0ca",
+   "913cbc3d2aaf724e70108e7854f56ad5bb9b2283",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-getTransceivers-expected.txt": [
@@ -386561,7 +386705,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-peerIdentity.html": [
-   "1cc5702e0aee887d925d2bf3471ac759d7430874",
+   "5aa9f3d712dd320cc85645abd39f960b5072349b",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-removeTrack.https-expected.txt": [
@@ -386577,7 +386721,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setDescription-transceiver.html": [
-   "aab677c9196488544b30c7eecd180c3046290bb2",
+   "4d0d9a168327e62eefc0d4398874fd944c50b43c",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt": [
@@ -386785,7 +386929,7 @@
    "support"
   ],
   "webrtc/RTCRtpReceiver-getSynchronizationSources.https.html": [
-   "56d0157f4ce987436c12ddff886b74549abbe682",
+   "11aa1d9f6833dd019ae7ade7b9ec14780f271650",
    "testharness"
   ],
   "webrtc/RTCRtpSender-getCapabilities-expected.txt": [
@@ -386801,7 +386945,7 @@
    "support"
   ],
   "webrtc/RTCRtpSender-getStats.https.html": [
-   "64c4424e36c566294a317fb423eb02e97a9ebbca",
+   "ee215306e1d9d1fdcb65bd5244da09fb2005e799",
    "testharness"
   ],
   "webrtc/RTCRtpSender-replaceTrack-expected.txt": [
@@ -394264,6 +394408,10 @@
    "42182437d8c1015339825c035127877f4970decb",
    "testharness"
   ],
+  "xhr/sync-no-progress.any.js": [
+   "db6171c61a37cd98ea5b9c1549432f1e2b53e7eb",
+   "testharness"
+  ],
   "xhr/template-element.html": [
    "748f12beaa646e244f8312afd545f56075cac727",
    "testharness"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/README.md b/third_party/WebKit/LayoutTests/external/wpt/README.md
index e43a05f..2a0007c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/README.md
+++ b/third_party/WebKit/LayoutTests/external/wpt/README.md
@@ -69,22 +69,19 @@
 ```
 
 This will start HTTP servers on two ports and a websockets server on
-one port. By default the web servers start on ports 8000 and 8443 and the other
-ports are randomly-chosen free ports. Tests must be loaded from the
-*first* HTTP server in the output. To change the ports, copy the
-`config.default.json` file to `config.json` and edit the new file,
-replacing the part that reads:
+one port. By default the web servers start on ports 8000 and 8443 and
+the other ports are randomly-chosen free ports. Tests must be loaded
+from the *first* HTTP server in the output. To change the ports,
+create a `config.json` file in the wpt root directory, and add
+port definitions of your choice e.g.:
 
 ```
-"http": [8000, "auto"],
-"https":[8443]
-```
-
-to some ports of your choice e.g.
-
-```
-"http": [1234, "auto"],
-"https":[5678]
+{
+  "ports": {
+    "http": [1234, "auto"],
+    "https":[5678]
+  }
+}
 ```
 
 After your `hosts` file is configured, the servers will be locally accessible at:
@@ -262,11 +259,26 @@
 error when you start wptserve.
 
 Finally, set the path value in the server configuration file to the
-default OpenSSL configuration file location. To do this,
-copy `config.default.json` in the web-platform-tests root to `config.json`.
-Then edit the JSON so that the key `ssl/openssl/base_conf_path` has a
-value that is the path to the OpenSSL config file (typically this
-will be `C:\\OpenSSL-Win32\\bin\\openssl.cfg`).
+default OpenSSL configuration file location. To do this create a file
+called `config.json`.  Then add the OpenSSL configuration below,
+ensuring that the key `ssl/openssl/base_conf_path` has a value that is
+the path to the OpenSSL config file (typically this will be
+`C:\\OpenSSL-Win32\\bin\\openssl.cfg`):
+
+```
+{
+  "ssl": {
+    "type": "openssl",
+    "encrypt_after_connect": false,
+    "openssl": {
+      "openssl_binary": "openssl",
+      "base_path: "_certs",
+      "force_regenerate": false,
+      "base_conf_path": "C:\\OpenSSL-Win32\\bin\\openssl.cfg"
+    },
+  },
+}
+```
 
 ### Trusting Root CA
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https-expected.txt
index cfcbdbf..f74a581 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https-expected.txt
@@ -1,10 +1,12 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: Feature-Policy header accelerometer 'none' disallows the top-level document.
 PASS Accelerometer: Feature-Policy header accelerometer 'none' disallows same-origin iframes.
 PASS Accelerometer: Feature-Policy header accelerometer 'none' disallows cross-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'none' disallows the top-level document.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'none' disallows same-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'none' disallows cross-origin iframes.
+FAIL GravitySensor: Feature-Policy header accelerometer 'none' disallows the top-level document. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer 'none' disallows same-origin iframes. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer 'none' disallows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https.html
index 9476efb..041ddc4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-disabled-by-feature-policy.https.html
@@ -8,8 +8,8 @@
 <script>
 "use strict";
 
-run_fp_tests_disabled(Accelerometer);
-run_fp_tests_disabled(LinearAccelerationSensor);
-run_fp_tests_disabled(GravitySensor);
+run_fp_tests_disabled('Accelerometer');
+run_fp_tests_disabled('LinearAccelerationSensor');
+run_fp_tests_disabled('GravitySensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
index 6647e64..15433268 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
@@ -1,8 +1,9 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: Feature-Policy allow='accelerometer' attribute allows same-origin relocation
 PASS Accelerometer: Feature-Policy allow='accelerometer' attribute disallows cross-origin relocation
 PASS LinearAccelerationSensor: Feature-Policy allow='accelerometer' attribute allows same-origin relocation
 PASS LinearAccelerationSensor: Feature-Policy allow='accelerometer' attribute disallows cross-origin relocation
+FAIL GravitySensor: Feature-Policy allow='accelerometer' attribute allows same-origin relocation assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy allow='accelerometer' attribute disallows cross-origin relocation assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
index aa8ea10..53f6960 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
@@ -8,8 +8,8 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute_redirect_on_load(Accelerometer);
-run_fp_tests_enabled_by_attribute_redirect_on_load(LinearAccelerationSensor);
-run_fp_tests_enabled_by_attribute_redirect_on_load(GravitySensor);
+run_fp_tests_enabled_by_attribute_redirect_on_load('Accelerometer');
+run_fp_tests_enabled_by_attribute_redirect_on_load('LinearAccelerationSensor');
+run_fp_tests_enabled_by_attribute_redirect_on_load('GravitySensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https-expected.txt
index 7a276b03..54b1ecd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https-expected.txt
@@ -1,8 +1,9 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: Feature-Policy allow='accelerometer' attribute allows same-origin iframe
 PASS Accelerometer: Feature-Policy allow='accelerometer' attribute allows cross-origin iframe
 PASS LinearAccelerationSensor: Feature-Policy allow='accelerometer' attribute allows same-origin iframe
 PASS LinearAccelerationSensor: Feature-Policy allow='accelerometer' attribute allows cross-origin iframe
+FAIL GravitySensor: Feature-Policy allow='accelerometer' attribute allows same-origin iframe assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy allow='accelerometer' attribute allows cross-origin iframe assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https.html
index 860a027..3dd49d3 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy-attribute.https.html
@@ -8,8 +8,8 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute(Accelerometer);
-run_fp_tests_enabled_by_attribute(LinearAccelerationSensor);
-run_fp_tests_enabled_by_attribute(GravitySensor);
+run_fp_tests_enabled_by_attribute('Accelerometer');
+run_fp_tests_enabled_by_attribute('LinearAccelerationSensor');
+run_fp_tests_enabled_by_attribute('GravitySensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https-expected.txt
index e4db6df..ceb8850 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https-expected.txt
@@ -1,10 +1,12 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: Feature-Policy header accelerometer * allows the top-level document.
 PASS Accelerometer: Feature-Policy header accelerometer * allows same-origin iframes.
 PASS Accelerometer: Feature-Policy header accelerometer * allows cross-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer * allows the top-level document.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer * allows same-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer * allows cross-origin iframes.
+FAIL GravitySensor: Feature-Policy header accelerometer * allows the top-level document. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer * allows same-origin iframes. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer * allows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https.html
index 889047a0..f5d0897 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-by-feature-policy.https.html
@@ -8,8 +8,8 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled(Accelerometer);
-run_fp_tests_enabled(LinearAccelerationSensor);
-run_fp_tests_enabled(GravitySensor);
+run_fp_tests_enabled('Accelerometer');
+run_fp_tests_enabled('LinearAccelerationSensor');
+run_fp_tests_enabled('GravitySensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
index b196236..b22f0e5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
@@ -1,10 +1,12 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: Feature-Policy header accelerometer 'self' allows the top-level document.
 PASS Accelerometer: Feature-Policy header accelerometer 'self' allows same-origin iframes.
 PASS Accelerometer: Feature-Policy header accelerometer 'self' disallows cross-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'self' allows the top-level document.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'self' allows same-origin iframes.
 PASS LinearAccelerationSensor: Feature-Policy header accelerometer 'self' disallows cross-origin iframes.
+FAIL GravitySensor: Feature-Policy header accelerometer 'self' allows the top-level document. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer 'self' allows same-origin iframes. assert_true: expected true got false
+FAIL GravitySensor: Feature-Policy header accelerometer 'self' disallows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html
index bf45852..2074562 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer-enabled-on-self-origin-by-feature-policy.https.html
@@ -8,8 +8,8 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_on_self_origin(Accelerometer);
-run_fp_tests_enabled_on_self_origin(LinearAccelerationSensor);
-run_fp_tests_enabled_on_self_origin(GravitySensor);
+run_fp_tests_enabled_on_self_origin('Accelerometer');
+run_fp_tests_enabled_on_self_origin('LinearAccelerationSensor');
+run_fp_tests_enabled_on_self_origin('GravitySensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https-expected.txt
index 8649567..f9d1e1a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https-expected.txt
@@ -1,20 +1,45 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
-FAIL Accelerometer: Test that 'onreading' is called and sensor reading is valid assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: sensor reading is correct assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: sensor timestamp is updated when time passes assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: Test that sensor can be successfully created and its states are correct. assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Accelerometer: sensor.start() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Accelerometer: no exception is thrown when calling start() on already started sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Accelerometer: sensor.stop() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Accelerometer: no exception is thrown when calling stop() on already stopped sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Accelerometer: Test that fresh reading is fetched on start() assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: sensor readings can not be fired on the background tab assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: frequency hint works promise_test: Unhandled rejection with value: object "[object SensorErrorEvent]"
-FAIL Accelerometer: sensor receives suspend / resume notifications when  cross-origin subframe is focused assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: throw 'NotSupportedError' for an unsupported sensor option assert_throws: function "() => { new sensorType({invalid: 1}) }" did not throw
+PASS Accelerometer: Test that 'onreading' is called and sensor reading is valid
+PASS Accelerometer: sensor reading is correct
+PASS Accelerometer: sensor timestamp is updated when time passes
+PASS Accelerometer: Test that sensor can be successfully created and its states are correct.
+PASS Accelerometer: sensor.start() returns undefined
+PASS Accelerometer: no exception is thrown when calling start() on already started sensor
+PASS Accelerometer: sensor.stop() returns undefined
+PASS Accelerometer: no exception is thrown when calling stop() on already stopped sensor
+PASS Accelerometer: Test that fresh reading is fetched on start()
+PASS Accelerometer: frequency hint works
+PASS Accelerometer: sensor receives suspend / resume notifications when  cross-origin subframe is focused
 FAIL Accelerometer: throw 'TypeError' if frequency is invalid assert_throws: when freq is undefined function "() => { new sensorType({frequency: freq}) }" did not throw
-FAIL Accelerometer: sensor reading is correct when options.referenceFrame is 'screen' assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Accelerometer: throw 'TypeError' if referenceFrame is not one of enumeration values assert_throws: when refFrame is undefined function "() => { new sensorType({referenceFrame: refFrame}) }" did not throw
+PASS Accelerometer: sensor reading is correct when options.referenceFrame is 'screen'
+PASS Accelerometer: throw 'TypeError' if referenceFrame is not one of enumeration values
+FAIL GravitySensor: Test that 'onreading' is called and sensor reading is valid assert_true: expected true got false
+FAIL GravitySensor: sensor reading is correct assert_true: expected true got false
+FAIL GravitySensor: sensor timestamp is updated when time passes assert_true: expected true got false
+FAIL GravitySensor: Test that sensor can be successfully created and its states are correct. assert_true: expected true got false
+FAIL GravitySensor: sensor.start() returns undefined assert_true: expected true got false
+FAIL GravitySensor: no exception is thrown when calling start() on already started sensor assert_true: expected true got false
+FAIL GravitySensor: sensor.stop() returns undefined assert_true: expected true got false
+FAIL GravitySensor: no exception is thrown when calling stop() on already stopped sensor assert_true: expected true got false
+FAIL GravitySensor: Test that fresh reading is fetched on start() assert_true: expected true got false
+FAIL GravitySensor: frequency hint works assert_true: expected true got false
+FAIL GravitySensor: sensor receives suspend / resume notifications when  cross-origin subframe is focused assert_true: expected true got false
+FAIL GravitySensor: throw 'TypeError' if frequency is invalid assert_true: expected true got false
+FAIL GravitySensor: sensor reading is correct when options.referenceFrame is 'screen' assert_true: expected true got false
+FAIL GravitySensor: throw 'TypeError' if referenceFrame is not one of enumeration values assert_true: expected true got false
+PASS LinearAccelerationSensor: Test that 'onreading' is called and sensor reading is valid
+PASS LinearAccelerationSensor: sensor reading is correct
+PASS LinearAccelerationSensor: sensor timestamp is updated when time passes
+PASS LinearAccelerationSensor: Test that sensor can be successfully created and its states are correct.
+PASS LinearAccelerationSensor: sensor.start() returns undefined
+PASS LinearAccelerationSensor: no exception is thrown when calling start() on already started sensor
+PASS LinearAccelerationSensor: sensor.stop() returns undefined
+PASS LinearAccelerationSensor: no exception is thrown when calling stop() on already stopped sensor
+PASS LinearAccelerationSensor: Test that fresh reading is fetched on start()
+PASS LinearAccelerationSensor: frequency hint works
+PASS LinearAccelerationSensor: sensor receives suspend / resume notifications when  cross-origin subframe is focused
+FAIL LinearAccelerationSensor: throw 'TypeError' if frequency is invalid assert_throws: when freq is undefined function "() => { new sensorType({frequency: freq}) }" did not throw
+PASS LinearAccelerationSensor: sensor reading is correct when options.referenceFrame is 'screen'
+PASS LinearAccelerationSensor: throw 'TypeError' if referenceFrame is not one of enumeration values
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https.html
index 9563270..b601c61 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer.https.html
@@ -9,8 +9,8 @@
 <div id="log"></div>
 <script>
 
-runGenericSensorTests(Accelerometer);
-runGenericSensorTests(GravitySensor);
-runGenericSensorTests(LinearAccelerationSensor);
+runGenericSensorTests('Accelerometer');
+runGenericSensorTests('GravitySensor');
+runGenericSensorTests('LinearAccelerationSensor');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https-expected.txt
index d0f2a19..54466064 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https-expected.txt
@@ -1,5 +1,6 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: GravitySensor is not defined
 PASS Accelerometer: 'onerror' event is fired when sensor is not supported
+FAIL GravitySensor: 'onerror' event is fired when sensor is not supported assert_true: expected true got false
+PASS LinearAccelerationSensor: 'onerror' event is fired when sensor is not supported
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https.html
index f69b1b6..dec2188 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/accelerometer/Accelerometer_onerror-manual.https.html
@@ -15,8 +15,8 @@
 </ol>
 <script>
 
-runGenericSensorOnerror(Accelerometer);
-runGenericSensorOnerror(GravitySensor);
-runGenericSensorOnerror(LinearAccelerationSensor);
+runGenericSensorOnerror('Accelerometer');
+runGenericSensorOnerror('GravitySensor');
+runGenericSensorOnerror('LinearAccelerationSensor');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot.js
index deeeaa0..5b048d33 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot.js
@@ -4,6 +4,6 @@
 // which found out that some script resources are served
 // with text/html content-type and with a body that is
 // both a valid html and a valid javascript.
-window.polyglot = 123;
+window.polyglot = "html-js-polyglot.js";
 
 //--></script></body></html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js
new file mode 100644
index 0000000..9443f8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js
@@ -0,0 +1,10 @@
+<!-- comment --> <script type='text/javascript'>
+//<![CDATA[
+
+// This is a regression test for https://crbug.com/839945
+// which found out that some script resources are served
+// with text/html content-type and with a body that is
+// both a valid html and a valid javascript.
+window.polyglot = "html-js-polyglot2.js";
+
+//]]>--></script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js.headers b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js.headers
new file mode 100644
index 0000000..156209f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/resources/html-js-polyglot2.js.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/script-html-js-polyglot.sub.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/script-html-js-polyglot.sub.html
index 950a907..ec41037 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/script-html-js-polyglot.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/corb/script-html-js-polyglot.sub.html
@@ -7,23 +7,26 @@
 <script src=/resources/testharnessreport.js></script>
 <div id=log></div>
 <script>
-async_test(function(t) {
-  var script = document.createElement("script")
+["html-js-polyglot.js", "html-js-polyglot2.js"].forEach(polyglot_name => {
+  async_test(function(t) {
+    window.polyglot = "not yet set by the script";
+    var script = document.createElement("script");
 
-  script.onload = t.step_func_done(function(){
-    // Verify that html-js-polyglot.js wasn't blocked - that script
-    // should have set window.polyglot to 123.
-    assert_equals(window.polyglot, 123);
-  })
-  addEventListener("error",function(e) {
-    t.step(function() {
-      assert_unreached("No errors are expected with or without CORB.");
-      t.done();
+    script.onload = t.step_func_done(function(){
+      // Verify that the script response wasn't blocked - that script
+      // should have set window.polyglot to |polyglot_name|.
+      assert_equals(window.polyglot, polyglot_name);
     })
-  });
+    addEventListener("error",function(e) {
+      t.step(function() {
+        assert_unreached("No errors are expected with or without CORB.");
+        t.done();
+      })
+    });
 
-  // www1 is cross-origin, so the HTTP response is CORB-eligible.
-  script.src = "http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/html-js-polyglot.js"
-  document.body.appendChild(script)
-}, "CORB cannot block polyglot HTML/JS");
+    // www1 is cross-origin, so the HTTP response is CORB-eligible.
+    script.src = "http://{{domains[www1]}}:{{ports[http][0]}}/fetch/corb/resources/" + polyglot_name;
+    document.body.appendChild(script);
+  }, "CORB cannot block polyglot HTML/JS: " + polyglot_name);
+});
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/README.md b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/README.md
new file mode 100644
index 0000000..993e0c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/README.md
@@ -0,0 +1,20 @@
+The `generic-sensor-tests.js` tests require an implementation of
+the `GenericSensorTest` interface, which should emulate platform
+sensor backends. The `GenericSensorTest` interface is defined as:
+
+```
+  class GenericSensorTest {
+    async initialize();  // Sets up the testing enviroment.
+    async reset(); // Frees the resources.
+  };
+```
+
+The Chromium implementation of the `GenericSensorTest` interface is located in
+[generic_sensor_mocks.js](../resources/chromium/generic_sensor_mocks.js).
+
+Other browser vendors should provide their own implementations of
+the `GenericSensorTest` interface.
+
+[Known issue](https://github.com/w3c/web-platform-tests/issues/9686): a
+WebDriver extension is a better approach for the Generic Sensor tests
+automation.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-feature-policy-test.sub.js b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-feature-policy-test.sub.js
index f722315..94e5347 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-feature-policy-test.sub.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-feature-policy-test.sub.js
@@ -17,17 +17,19 @@
   "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src;
 const base_src = "/feature-policy/resources/redirect-on-load.html#";
 
-function run_fp_tests_disabled(sensorType) {
-  const sensorName = sensorType.name;
+function run_fp_tests_disabled(sensorName) {
+  const sensorType = self[sensorName];
   const featureNameList = feature_policies[sensorName];
   const header = "Feature-Policy header " + featureNameList.join(" 'none';") + " 'none'";
   const desc = "'new " + sensorName + "()'";
 
   test(() => {
+    assert_true(sensorName in self);
     assert_throws("SecurityError", () => {new sensorType()});
   }, `${sensorName}: ${header} disallows the top-level document.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -37,6 +39,7 @@
   }, `${sensorName}: ${header} disallows same-origin iframes.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -46,17 +49,18 @@
   }, `${sensorName}: ${header} disallows cross-origin iframes.`);
 }
 
-function run_fp_tests_enabled(sensorType) {
-  const sensorName = sensorType.name;
+function run_fp_tests_enabled(sensorName) {
+  const sensorType = self[sensorName];
   const featureNameList = feature_policies[sensorName];
   const header = "Feature-Policy header " + featureNameList.join(" *;") + " *";
   const desc = "'new " + sensorName + "()'";
 
   test(() => {
-    assert_true(sensorName in window);
+    assert_true(sensorName in self);
   }, `${sensorName}: ${header} allows the top-level document.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -66,6 +70,7 @@
   }, `${sensorName}: ${header} allows same-origin iframes.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -75,13 +80,14 @@
   }, `${sensorName}: ${header} allows cross-origin iframes.`);
 }
 
-function run_fp_tests_enabled_by_attribute(sensorType) {
-  const sensorName = sensorType.name;
+function run_fp_tests_enabled_by_attribute(sensorName) {
+  const sensorType = self[sensorName];
   const featureNameList = feature_policies[sensorName];
   const header = "Feature-Policy allow='" + featureNameList.join(" ") + "' attribute";
   const desc = "'new " + sensorName + "()'";
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -92,6 +98,7 @@
   }, `${sensorName}: ${header} allows same-origin iframe`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -102,13 +109,14 @@
   }, `${sensorName}: ${header} allows cross-origin iframe`);
 }
 
-function run_fp_tests_enabled_by_attribute_redirect_on_load(sensorType) {
-  const sensorName = sensorType.name;
+function run_fp_tests_enabled_by_attribute_redirect_on_load(sensorName) {
+  const sensorType = self[sensorName];
   const featureNameList = feature_policies[sensorName];
   const header = "Feature-Policy allow='" + featureNameList.join(" ") + "' attribute";
   const desc = "'new " + sensorName + "()'";
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -119,6 +127,7 @@
   }, `${sensorName}: ${header} allows same-origin relocation`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -129,17 +138,18 @@
   }, `${sensorName}: ${header} disallows cross-origin relocation`);
 }
 
-function run_fp_tests_enabled_on_self_origin(sensorType) {
-  const sensorName = sensorType.name;
+function run_fp_tests_enabled_on_self_origin(sensorName) {
+  const sensorType = self[sensorName];
   const featureNameList = feature_policies[sensorName];
   const header = "Feature-Policy header " + featureNameList.join(" 'self';") + " 'self'";
   const desc = "'new " + sensorName + "()'";
 
   test(() => {
-    assert_true(sensorName in window);
+    assert_true(sensorName in self);
   }, `${sensorName}: ${header} allows the top-level document.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
@@ -149,6 +159,7 @@
   }, `${sensorName}: ${header} allows same-origin iframes.`);
 
   async_test(t => {
+    assert_true(sensorName in self);
     test_feature_availability(
       desc,
       t,
diff --git a/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-tests.js b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-tests.js
index ece481b..da2e65d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-tests.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/generic-sensor/generic-sensor-tests.js
@@ -1,3 +1,54 @@
+// These tests rely on the User Agent providing an implementation of
+// platform sensor backends.
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+//   --enable-blink-features=MojoJS,MojoJSTest
+let loadChromiumResources = Promise.resolve().then(() => {
+  if (!MojoInterfaceInterceptor) {
+    // Do nothing on non-Chromium-based browsers or when the Mojo bindings are
+    // not present in the global namespace.
+    return;
+  }
+
+  let chain = Promise.resolve();
+  [
+    '/resources/chromium/mojo_bindings.js',
+    '/resources/chromium/string16.mojom.js',
+    '/resources/chromium/sensor.mojom.js',
+    '/resources/chromium/sensor_provider.mojom.js',
+    '/resources/chromium/generic_sensor_mocks.js',
+  ].forEach(path => {
+    let script = document.createElement('script');
+    script.src = path;
+    script.async = false;
+    chain = chain.then(() => new Promise(resolve => {
+      script.onload = resolve;
+    }));
+    document.head.appendChild(script);
+  });
+
+  return chain;
+});
+
+function sensor_test(func, name, properties) {
+  promise_test(async (t) => {
+    if (typeof GenericSensorTest === 'undefined') {
+      await loadChromiumResources;
+    }
+    assert_true(typeof GenericSensorTest !== 'undefined');
+    let sensorTest = new GenericSensorTest();
+    await sensorTest.initialize();
+    try {
+      await func(t);
+    } finally {
+      await sensorTest.reset();
+    };
+  }, name, properties);
+}
+
 const properties = {
   'AmbientLightSensor' : ['timestamp', 'illuminance'],
   'Accelerometer' : ['timestamp', 'x', 'y', 'z'],
@@ -45,8 +96,11 @@
   return arr;
 }
 
-function runGenericSensorTests(sensorType) {
-  promise_test(async t => {
+function runGenericSensorTests(sensorName) {
+  const sensorType = self[sensorName];
+
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
     sensor.start();
@@ -58,9 +112,10 @@
     sensor.stop();
     assert_reading_null(sensor);
     assert_false(sensor.hasReading);
-  }, `${sensorType.name}: Test that 'onreading' is called and sensor reading is valid`);
+  }, `${sensorName}: Test that 'onreading' is called and sensor reading is valid`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor1 = new sensorType();
     const sensor2 = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor1, ["reading", "error"]);
@@ -79,9 +134,10 @@
     assert_reading_not_null(sensor2);
     sensor2.stop();
     assert_reading_null(sensor2);
-  }, `${sensorType.name}: sensor reading is correct`);
+  }, `${sensorName}: sensor reading is correct`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
     sensor.start();
@@ -94,9 +150,10 @@
 
     assert_greater_than(cachedTimeStamp2, cachedTimeStamp1);
     sensor.stop();
-  }, `${sensorType.name}: sensor timestamp is updated when time passes`);
+  }, `${sensorName}: sensor timestamp is updated when time passes`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]);
     assert_false(sensor.activated);
@@ -108,9 +165,10 @@
 
     sensor.stop();
     assert_false(sensor.activated);
-  }, `${sensorType.name}: Test that sensor can be successfully created and its states are correct.`);
+  }, `${sensorName}: Test that sensor can be successfully created and its states are correct.`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]);
     const start_return = sensor.start();
@@ -118,9 +176,10 @@
     await sensorWatcher.wait_for("activate");
     assert_equals(start_return, undefined);
     sensor.stop();
-  }, `${sensorType.name}: sensor.start() returns undefined`);
+  }, `${sensorName}: sensor.start() returns undefined`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]);
     sensor.start();
@@ -129,9 +188,10 @@
     await sensorWatcher.wait_for("activate");
     assert_true(sensor.activated);
     sensor.stop();
-  }, `${sensorType.name}: no exception is thrown when calling start() on already started sensor`);
+  }, `${sensorName}: no exception is thrown when calling start() on already started sensor`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]);
     sensor.start();
@@ -139,9 +199,10 @@
     await sensorWatcher.wait_for("activate");
     const stop_return = sensor.stop();
     assert_equals(stop_return, undefined);
-  }, `${sensorType.name}: sensor.stop() returns undefined`);
+  }, `${sensorName}: sensor.stop() returns undefined`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["activate", "error"]);
     sensor.start();
@@ -150,9 +211,10 @@
     sensor.stop();
     sensor.stop();
     assert_false(sensor.activated);
-  }, `${sensorType.name}: no exception is thrown when calling stop() on already stopped sensor`);
+  }, `${sensorName}: no exception is thrown when calling stop() on already stopped sensor`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
     sensor.start();
@@ -169,28 +231,30 @@
     assert_greater_than(timestamp, 0);
     assert_greater_than(sensor.timestamp, timestamp);
     sensor.stop();
-  }, `${sensorType.name}: Test that fresh reading is fetched on start()`);
+  }, `${sensorName}: Test that fresh reading is fetched on start()`);
 
-  promise_test(async t => {
-    const sensor = new sensorType();
-    const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
-    const visibilityChangeWatcher = new EventWatcher(t, document, "visibilitychange");
-    sensor.start();
+//  TBD file a WPT issue: visibilityChangeWatcher times out.
+//  sensor_test(async t => {
+//    const sensor = new sensorType();
+//    const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
+//    const visibilityChangeWatcher = new EventWatcher(t, document, "visibilitychange");
+//    sensor.start();
 
-    await sensorWatcher.wait_for("reading");
-    assert_reading_not_null(sensor);
-    const cachedSensor1 = reading_to_array(sensor);
+//    await sensorWatcher.wait_for("reading");
+//    assert_reading_not_null(sensor);
+//    const cachedSensor1 = reading_to_array(sensor);
 
-    const win = window.open('', '_blank');
-    await visibilityChangeWatcher.wait_for("visibilitychange");
-    const cachedSensor2 = reading_to_array(sensor);
+//    const win = window.open('', '_blank');
+//    await visibilityChangeWatcher.wait_for("visibilitychange");
+//    const cachedSensor2 = reading_to_array(sensor);
 
-    win.close();
-    sensor.stop();
-    assert_object_equals(cachedSensor1, cachedSensor2);
-  }, `${sensorType.name}: sensor readings can not be fired on the background tab`);
+//    win.close();
+//    sensor.stop();
+//    assert_object_equals(cachedSensor1, cachedSensor2);
+//  }, `${sensorName}: sensor readings can not be fired on the background tab`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const fastSensor = new sensorType({frequency: 30});
     const slowSensor = new sensorType({frequency: 5});
     slowSensor.start();
@@ -217,9 +281,10 @@
     });
     assert_greater_than(fastCounter, 2,
                         "Fast sensor overtakes the slow one");
-  }, `${sensorType.name}: frequency hint works`);
+  }, `${sensorName}: frequency hint works`);
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     // Create a focused editbox inside a cross-origin iframe,
     // sensor notification must suspend.
     const iframeSrc = 'data:text/html;charset=utf-8,<html><body>'
@@ -248,18 +313,20 @@
     assert_greater_than(sensor.timestamp, cachedTimestamp);
 
     sensor.stop();
-  }, `${sensorType.name}: sensor receives suspend / resume notifications when\
+  }, `${sensorName}: sensor receives suspend / resume notifications when\
   cross-origin subframe is focused`);
 
-  test(() => {
-     assert_throws("NotSupportedError", () => { new sensorType({invalid: 1}) });
-     assert_throws("NotSupportedError", () => { new sensorType({frequency: 60, invalid: 1}) });
-     if (spatialSensors.indexOf(sensorType.name) == -1) {
-       assert_throws("NotSupportedError", () => { new sensorType({referenceFrame: "screen"}) });
-     }
-  }, `${sensorType.name}: throw 'NotSupportedError' for an unsupported sensor option`);
+//  Re-enable after https://github.com/w3c/sensors/issues/361 is fixed.
+//  test(() => {
+//     assert_throws("NotSupportedError", () => { new sensorType({invalid: 1}) });
+//     assert_throws("NotSupportedError", () => { new sensorType({frequency: 60, invalid: 1}) });
+//     if (spatialSensors.indexOf(sensorName) == -1) {
+//       assert_throws("NotSupportedError", () => { new sensorType({referenceFrame: "screen"}) });
+//     }
+//  }, `${sensorName}: throw 'NotSupportedError' for an unsupported sensor option`);
 
   test(() => {
+    assert_true(sensorName in self);
     const invalidFreqs = [
       "invalid",
       NaN,
@@ -273,14 +340,15 @@
                     () => { new sensorType({frequency: freq}) },
                     `when freq is ${freq}`);
     });
-  }, `${sensorType.name}: throw 'TypeError' if frequency is invalid`);
+  }, `${sensorName}: throw 'TypeError' if frequency is invalid`);
 
-  if (spatialSensors.indexOf(sensorType.name) == -1) {
+  if (spatialSensors.indexOf(sensorName) == -1) {
     // The sensorType does not represent a spatial sensor.
     return;
   }
 
-  promise_test(async t => {
+  sensor_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType({referenceFrame: "screen"});
     const sensorWatcher = new EventWatcher(t, sensor, ["reading", "error"]);
     sensor.start();
@@ -291,34 +359,37 @@
     assert_reading_not_null(sensor);
 
     sensor.stop();
-  }, `${sensorType.name}: sensor reading is correct when options.referenceFrame is 'screen'`);
+  }, `${sensorName}: sensor reading is correct when options.referenceFrame is 'screen'`);
 
   test(() => {
+    assert_true(sensorName in self);
     const invalidRefFrames = [
       "invalid",
       null,
       123,
       {},
       "",
-      true,
-      undefined
+      true
     ];
     invalidRefFrames.map(refFrame => {
       assert_throws(new TypeError(),
                     () => { new sensorType({referenceFrame: refFrame}) },
                     `when refFrame is ${refFrame}`);
     });
-  }, `${sensorType.name}: throw 'TypeError' if referenceFrame is not one of enumeration values`);
+  }, `${sensorName}: throw 'TypeError' if referenceFrame is not one of enumeration values`);
 }
 
-function runGenericSensorInsecureContext(sensorType) {
+function runGenericSensorInsecureContext(sensorName) {
   test(() => {
-    assert_false(sensorType in window, `${sensorType} must not be exposed`);
-  }, `${sensorType} is not exposed in an insecure context`);
+    assert_false(sensorName in window, `${sensorName} must not be exposed`);
+  }, `${sensorName} is not exposed in an insecure context`);
 }
 
-function runGenericSensorOnerror(sensorType) {
+function runGenericSensorOnerror(sensorName) {
+  const sensorType = self[sensorName];
+
   promise_test(async t => {
+    assert_true(sensorName in self);
     const sensor = new sensorType();
     const sensorWatcher = new EventWatcher(t, sensor, ["error", "activate"]);
     sensor.start();
@@ -327,5 +398,5 @@
     assert_false(sensor.activated);
     assert_true(event.error.name == 'NotReadableError' ||
                 event.error.name == 'NotAllowedError');
-  }, `${sensorType.name}: 'onerror' event is fired when sensor is not supported`);
+  }, `${sensorName}: 'onerror' event is fired when sensor is not supported`);
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-disabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-disabled-by-feature-policy.https.html
index de4aee2d..02cc44e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-disabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-disabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_disabled(Gyroscope);
+run_fp_tests_disabled('Gyroscope');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute-redirect-on-load.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute-redirect-on-load.https.html
index 3c831bb7..e1aa0f94 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute-redirect-on-load.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute-redirect-on-load.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute_redirect_on_load(Gyroscope);
+run_fp_tests_enabled_by_attribute_redirect_on_load('Gyroscope');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute.https.html
index 46473c3a..17e5b27 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy-attribute.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute(Gyroscope);
+run_fp_tests_enabled_by_attribute('Gyroscope');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy.https.html
index 95eee33..9bcced92 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled(Gyroscope);
+run_fp_tests_enabled('Gyroscope');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html
index ae5a63d1..76d1c5e7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope-enabled-on-self-origin-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_on_self_origin(Gyroscope);
+run_fp_tests_enabled_on_self_origin('Gyroscope');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https-expected.txt
index 0121138..c673fd7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https-expected.txt
@@ -1,19 +1,17 @@
 This is a testharness.js-based test.
-FAIL Gyroscope: Test that 'onreading' is called and sensor reading is valid assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: sensor reading is correct assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: sensor timestamp is updated when time passes assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: Test that sensor can be successfully created and its states are correct. assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Gyroscope: sensor.start() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Gyroscope: no exception is thrown when calling start() on already started sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Gyroscope: sensor.stop() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Gyroscope: no exception is thrown when calling stop() on already stopped sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Gyroscope: Test that fresh reading is fetched on start() assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: sensor readings can not be fired on the background tab assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: frequency hint works promise_test: Unhandled rejection with value: object "[object SensorErrorEvent]"
-FAIL Gyroscope: sensor receives suspend / resume notifications when  cross-origin subframe is focused assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: throw 'NotSupportedError' for an unsupported sensor option assert_throws: function "() => { new sensorType({invalid: 1}) }" did not throw
+PASS Gyroscope: Test that 'onreading' is called and sensor reading is valid
+PASS Gyroscope: sensor reading is correct
+PASS Gyroscope: sensor timestamp is updated when time passes
+PASS Gyroscope: Test that sensor can be successfully created and its states are correct.
+PASS Gyroscope: sensor.start() returns undefined
+PASS Gyroscope: no exception is thrown when calling start() on already started sensor
+PASS Gyroscope: sensor.stop() returns undefined
+PASS Gyroscope: no exception is thrown when calling stop() on already stopped sensor
+PASS Gyroscope: Test that fresh reading is fetched on start()
+PASS Gyroscope: frequency hint works
+PASS Gyroscope: sensor receives suspend / resume notifications when  cross-origin subframe is focused
 FAIL Gyroscope: throw 'TypeError' if frequency is invalid assert_throws: when freq is undefined function "() => { new sensorType({frequency: freq}) }" did not throw
-FAIL Gyroscope: sensor reading is correct when options.referenceFrame is 'screen' assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Gyroscope: throw 'TypeError' if referenceFrame is not one of enumeration values assert_throws: when refFrame is undefined function "() => { new sensorType({referenceFrame: refFrame}) }" did not throw
+PASS Gyroscope: sensor reading is correct when options.referenceFrame is 'screen'
+PASS Gyroscope: throw 'TypeError' if referenceFrame is not one of enumeration values
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https.html
index 81cdfdd..0c3f08f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope.https.html
@@ -9,6 +9,6 @@
 <div id="log"></div>
 <script>
 
-runGenericSensorTests(Gyroscope);
+runGenericSensorTests('Gyroscope');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope_onerror-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope_onerror-manual.https.html
index f012615..b63448b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope_onerror-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/gyroscope/Gyroscope_onerror-manual.https.html
@@ -15,6 +15,6 @@
 </ol>
 <script>
 
-runGenericSensorOnerror(Gyroscope);
+runGenericSensorOnerror('Gyroscope');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/origin-of-data-document.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/origin-of-data-document.html
index 345a3a6..d6b6d53 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/origin-of-data-document.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/origin-of-data-document.html
@@ -20,16 +20,7 @@
           assert_throws("SecurityError", function () {
             var couldAccessCrossOriginProperty = e.source.location.href;
           }, "The 'data:' frame should be cross-origin: 'window.location.href'");
-
-          // Try to access contentDocument of the 'data: ' frame. Some browsers
-          // (i.e. Firefox, Safari) will return |null| and some (i.e. Chrome)
-          // will throw an exception.
-          var dataFrameContentDocument = null;
-          try {
-            dataFrameContentDocument = i.contentDocument;
-          } catch (ex) {
-          }
-          assert_equals(dataFrameContentDocument, null, "The 'data:' iframe should be unable to access its contentDocument.");
+          assert_equals(i.contentDocument, null, "The 'data:' iframe should be unable to access its contentDocument.");
         }));
 
         document.body.appendChild(i);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist b/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
index e7e1af8..ce825bfa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
+++ b/third_party/WebKit/LayoutTests/external/wpt/lint.whitelist
@@ -10,18 +10,13 @@
 INDENT TABS: .gitmodules
 INDENT TABS: conformance-checkers/*
 INDENT TABS: content-security-policy/*
-INDENT TABS: custom-elements/*
-INDENT TABS: old-tests/*
 INDENT TABS: pointerlock/*
 INDENT TABS: shadow-dom/*
-INDENT TABS: tools/*
-INDENT TABS: web-animations/*
 INDENT TABS: webaudio/*
 INDENT TABS: webvtt/*
 INDENT TABS: encoding/legacy*/*
 
 TRAILING WHITESPACE: 2dcontext/tools/current-work-canvas.xhtml
-TRAILING WHITESPACE: battery-status/*
 TRAILING WHITESPACE: conformance-checkers/*
 TRAILING WHITESPACE: content-security-policy/*
 TRAILING WHITESPACE: custom-elements/*
@@ -29,11 +24,9 @@
 TRAILING WHITESPACE: old-tests/*
 TRAILING WHITESPACE: pointerevents/*
 TRAILING WHITESPACE: shadow-dom/*
-TRAILING WHITESPACE: tools/*
 TRAILING WHITESPACE: webaudio/*
 TRAILING WHITESPACE: WebIDL/*
 TRAILING WHITESPACE: webvtt/*
-TRAILING WHITESPACE: encoding/legacy*/*
 TRAILING WHITESPACE: server-timing/resources/parsing/*.sub.headers
 
 ## File types that should never be checked ##
@@ -77,9 +70,6 @@
 
 ## Helper scripts ##
 
-W3C-TEST.ORG: tools/*
-PRINT STATEMENT: tools/*
-W3C-TEST.ORG: */tools/*
 PRINT STATEMENT: */tools/*
 
 ## Deliberate copies of Ahem ##
@@ -101,7 +91,6 @@
 CR AT EOL: html/form-elements/the-textarea-element/multiline-placeholder-crlf.html
 CR AT EOL: html/input/the-placeholder-attribute/multiline-cr.html
 CR AT EOL: html/input/the-placeholder-attribute/multiline-crlf.html
-CR AT EOL: html/semantics/embedded-content/the-canvas-element/size.attributes.parse.whitespace.html
 CR AT EOL: webvtt/parsing/file-parsing/tests/support/newlines.vtt
 
 # Intentional use of tabs
@@ -139,7 +128,6 @@
 
 # setTimeout usage (should probably mostly be fixed)
 SET TIMEOUT: *-manual.*
-SET TIMEOUT: 2dcontext/*
 SET TIMEOUT: annotation-model/scripts/ajv.min.js
 SET TIMEOUT: apng/animated-png-timeout.html
 SET TIMEOUT: cookies/resources/testharness-helpers.js
@@ -181,18 +169,13 @@
 SET TIMEOUT: navigation-timing/*
 SET TIMEOUT: offscreen-canvas/the-offscreen-canvas/*
 SET TIMEOUT: old-tests/submission/Microsoft/history/history_000.htm
-SET TIMEOUT: page-visibility/resources/pagevistestharness.js
 SET TIMEOUT: paint-timing/resources/subframe-painting.html
 SET TIMEOUT: payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html
-SET TIMEOUT: payment-request/payment-request-response-id.html
-SET TIMEOUT: pointerevents/pointerevent_support.js
 SET TIMEOUT: preload/single-download-preload.html
 SET TIMEOUT: resize-observer/resources/iframe.html
 SET TIMEOUT: resource-timing/resource-timing.js
 SET TIMEOUT: resource-timing/single-entry-per-resource.html
-SET TIMEOUT: screen-orientation/lock-bad-argument.html
 SET TIMEOUT: screen-orientation/onchange-event.html
-SET TIMEOUT: screen-orientation/resources/sandboxed-iframe-locking.html
 SET TIMEOUT: secure-contexts/basic-popup-and-iframe-tests.https.js
 SET TIMEOUT: service-workers/cache-storage/script-tests/cache-abort.js
 SET TIMEOUT: service-workers/service-worker/activation.https.html
@@ -208,12 +191,10 @@
 SET TIMEOUT: service-workers/service-worker/resources/opaque-response-preloaded-xhr.html
 SET TIMEOUT: service-workers/service-worker/resources/performance-timeline-worker.js
 SET TIMEOUT: service-workers/service-worker/resources/resource-timing-worker.js
-SET TIMEOUT: service-workers/service-worker/resources/register-foreign-fetch-errors-worker.js
 SET TIMEOUT: shadow-dom/Document-prototype-currentScript.html
 SET TIMEOUT: shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
 SET TIMEOUT: shadow-dom/slotchange-event.html
 SET TIMEOUT: shadow-dom/untriaged/html-elements-in-shadow-trees/html-forms/test-003.html
-SET TIMEOUT: shadow-dom/untriaged/html-elements-in-shadow-trees/inert-html-elements/test-001.html
 SET TIMEOUT: streams/piping/close-propagation-forward.js
 SET TIMEOUT: streams/piping/error-propagation-backward.js
 SET TIMEOUT: streams/piping/error-propagation-forward.js
@@ -222,12 +203,10 @@
 SET TIMEOUT: streams/resources/rs-utils.js
 SET TIMEOUT: streams/writable-streams/byte-length-queuing-strategy.js
 SET TIMEOUT: user-timing/*
-SET TIMEOUT: webaudio/js/lodash.js
 SET TIMEOUT: webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html
 SET TIMEOUT: webauthn/*timeout.https.html
 SET TIMEOUT: webdriver/*
 SET TIMEOUT: webmessaging/*
-SET TIMEOUT: websockets/*
 SET TIMEOUT: webstorage/eventTestHarness.js
 SET TIMEOUT: webvtt/*
 SET TIMEOUT: workers/*
@@ -250,17 +229,13 @@
 GENERATE_TESTS: css/css-tables/height-distribution/computing-row-measure-0.html
 GENERATE_TESTS: css/css-tables/height-distribution/computing-row-measure-1.html
 GENERATE_TESTS: css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html
-GENERATE_TESTS: css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html
 GENERATE_TESTS: css/css-tables/html-to-css-mapping-1.html
 GENERATE_TESTS: css/css-tables/html-to-css-mapping-2.html
 GENERATE_TESTS: css/css-tables/html5-table-formatting-1.html
-GENERATE_TESTS: css/css-tables/html5-table-formatting-1.html
 GENERATE_TESTS: css/css-tables/html5-table-formatting-2.html
 GENERATE_TESTS: css/css-tables/html5-table-formatting-3.html
 GENERATE_TESTS: css/css-tables/html5-table-formatting-fixed-layout-1.html
 GENERATE_TESTS: css/css-tables/table-model-fixup-2.html
-GENERATE_TESTS: css/css-tables/table-model-fixup-2.html
-GENERATE_TESTS: css/css-tables/table-model-fixup-2.html
 GENERATE_TESTS: css/css-tables/table-model-fixup.html
 GENERATE_TESTS: css/css-tables/visibility-collapse-col-001.html
 GENERATE_TESTS: css/css-tables/visibility-collapse-row-001.html
@@ -323,12 +298,8 @@
 SET TIMEOUT: acid/acid3/test.html
 
 # Travis
-W3C-TEST.ORG: .travis.yml
 WEB-PLATFORM.TEST: .travis.yml
 
-# Config
-WEB-PLATFORM.TEST: config.default.json
-WEB-PLATFORM.TEST: resources/test/config.test.json
 
 # Third party code
 *: css/tools/apiclient/*
@@ -339,29 +310,22 @@
 # Build system virtualenv
 *: css/tools/_virtualenv/*
 
-
 ## Third party data files
 TRAILING WHITESPACE: css/css-writing-modes/tools/generators/ucd/Blocks.txt
 TRAILING WHITESPACE: resources/chromium/*
 
-
 ## Test generation files
 CONSOLE: css/css-writing-modes/tools/generators/unicode-data.js
 
-
 ## Test plans and implementation reports
-*: css/*/reports/*
 *: css/*/test-plan/*
 
-
 ## Things we don't have enabled yet
 OPEN-NO-MODE: css/*
-PARSE-FAILED: css/*
 PRINT STATEMENT: css/*
 CONTENT-VISUAL: css/*
 CONTENT-MANUAL: css/*
 
-
 ## Support files not in /support/ or similar
 SUPPORT-WRONG-DIR: css/requirements.txt
 SUPPORT-WRONG-DIR: css/README.md
@@ -374,10 +338,8 @@
 SUPPORT-WRONG-DIR: css/*-README
 SUPPORT-WRONG-DIR: css/*/LICENSE
 SUPPORT-WRONG-DIR: css/*/LICENSE-*
-SUPPORT-WRONG-DIR: css/*/COPYING
 SUPPORT-WRONG-DIR: css/*/Makefile
 SUPPORT-WRONG-DIR: css/*/OWNERS
-SUPPORT-WRONG-DIR: fonts/*
 
 # The selectors-3 testsuite has a weird build system
 SUPPORT-WRONG-DIR: css/selectors/*
@@ -420,8 +382,6 @@
 INDENT TABS: css/css-lists/*
 INDENT TABS: css/css-masking/*
 INDENT TABS: css/css-multicol/*
-INDENT TABS: css/cssom/*
-INDENT TABS: css/cssom-view/*
 INDENT TABS: css/css-page/*
 INDENT TABS: css/css-pseudo/*
 INDENT TABS: css/css-regions/*
@@ -437,7 +397,6 @@
 INDENT TABS: css/css-variables/*
 INDENT TABS: css/css-writing-modes/*
 INDENT TABS: css/filter-effects/*
-INDENT TABS: fonts/*
 INDENT TABS: css/mediaqueries/*
 INDENT TABS: css/selectors/*
 INDENT TABS: css/vendor-imports/*
@@ -474,10 +433,7 @@
 TRAILING WHITESPACE: css/css-scoping/css-scoping-shadow-with-outside-rules.html
 TRAILING WHITESPACE: css/css-scoping/css-scoping-shadow-with-rules.html
 TRAILING WHITESPACE: css/css-scoping/css-scoping-shadow-with-rules-no-style-leak.html
-TRAILING WHITESPACE: css/css-transforms/css3-transform-rotateY.html
 TRAILING WHITESPACE: css/css-variables/reference/vars-font-shorthand-001-ref.html
-TRAILING WHITESPACE: css/css-variables/vars-font-shorthand-001.html
-TRAILING WHITESPACE: css/geometry/DOMMatrix-001.html
 TRAILING WHITESPACE: css/vendor-imports/mozilla/mozilla-central-reftests/css21/pagination/moz-css21-float-page-break-inside-avoid-7.html
 TRAILING WHITESPACE: css/vendor-imports/mozilla/mozilla-central-reftests/css21/pagination/moz-css21-float-page-break-inside-avoid-7-ref.html
 TRAILING WHITESPACE: css/vendor-imports/mozilla/mozilla-central-reftests/css21/pagination/moz-css21-float-page-break-inside-avoid-8.html
@@ -549,24 +505,10 @@
 SET TIMEOUT: css/CSS2/selectors/dom-hover-001.xht
 SET TIMEOUT: css/CSS2/selectors/dom-hover-002.xht
 SET TIMEOUT: css/CSS2/tables/tables-102.xht
-SET TIMEOUT: css/cssom-view/matchMediaAddListener.html
 SET TIMEOUT: css/mediaqueries/min-width-tables-001.html
-SET TIMEOUT: css/selectors/focus-within-shadow-001.html
-SET TIMEOUT: css/selectors/focus-within-shadow-002.html
-SET TIMEOUT: css/selectors/focus-within-shadow-003.html
-SET TIMEOUT: css/selectors/focus-within-shadow-004.html
-SET TIMEOUT: css/selectors/focus-within-shadow-005.html
 
 ## Build system stuff
-CSS-COLLIDING-TEST-NAME: css/*/OWNERS
-CSS-COLLIDING-TEST-NAME: css/*/README
-CSS-COLLIDING-TEST-NAME: css/*/README.md
-CSS-COLLIDING-TEST-NAME: css/*/LICENSE
-CSS-COLLIDING-TEST-NAME: css/*/Makefile
-CSS-COLLIDING-TEST-NAME: css/*/reftest.list
-CSS-COLLIDING-TEST-NAME: css/*/.htaccess
 CSS-COLLIDING-SUPPORT-NAME: css/*/README
-CSS-COLLIDING-SUPPORT-NAME: css/*/.htaccess
 CSS-COLLIDING-SUPPORT-NAME: css/*/LOCK
 
 # These are all the current "merge mismatch" errors the build system produces
@@ -664,95 +606,6 @@
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/tables/support/pattern-grg-rgr-grg.png
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/values/support/pattern-grg-rgr-grg.png
 
-# These are thrown off by their support/support/ copies
-# https:css///github.com/w3c/csswg-test/issues/1235
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/bidi-text/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/tables/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/lists/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/filter-effects/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/cssom-view/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-values/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-style-attr/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/text/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-multicol/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/values/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/box-display/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/generated-content/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/support/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-fonts/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/pagination/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-flexbox/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/fonts/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/selectors/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/ui/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/zindex/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/csswg-issues/submitted/css2.1/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/backgrounds/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/i18n/syndata/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-text/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-transforms/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/syntax/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-images/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-transitions/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/cascade/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/floats-clear/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/css1/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-shapes/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/colors/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/positioning/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-regions/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/borders/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/cssom/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-page/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/generate/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/normal-flow/support/swatch-red.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-flexbox/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/generated-content/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/backgrounds/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/text/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-values/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/csswg-issues/submitted/css2.1/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/borders/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/filter-effects/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/support/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/cssom/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-writing-modes/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/positioning/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-fonts/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-images/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/box-display/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/colors/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/generate/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-transforms/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/normal-flow/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-text/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-page/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/bidi-text/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-style-attr/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/zindex/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-transitions/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/fonts/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/i18n/syndata/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/selectors/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/syntax/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/css1/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-shapes/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/lists/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/ui/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/margin-padding-clear/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-backgrounds/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/tables/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/cascade/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-multicol/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/values/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/css-regions/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/floats-clear/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/cssom-view/support/swatch-green.png
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/pagination/support/swatch-green.png
-
 # Duplicate filename not picked up by the build system
 # https:css///github.com/w3c/csswg-test/issues/1236
 CSS-COLLIDING-REF-NAME: css/css-masking/clip-path-svg-content/reference/clip-path-square-001-ref.svg
@@ -794,7 +647,6 @@
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/ui/support/animated.gif
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/backgrounds/support/animated.gif
 CSS-COLLIDING-SUPPORT-NAME: css/css-shapes/shape-outside/shape-image/support/animated.gif
-CSS-COLLIDING-SUPPORT-NAME: css/CSS2/css1/support/pattern-gg-gr.png
 CSS-COLLIDING-SUPPORT-NAME: css/css-display/support/util.js
 CSS-COLLIDING-SUPPORT-NAME: css/CSS2/normal-flow/support/replaced-min-max-1.png
 CSS-COLLIDING-SUPPORT-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/ui3/support/replaced-min-max-1.png
@@ -885,7 +737,6 @@
 SUPPORT-WRONG-DIR: css/css-timing/testcommon.js
 MISSING-LINK: css/css-typed-om/CSSMatrixComponent-DOMMatrix-mutable.html
 MISSING-LINK: css/css-typed-om/declared-styleMap-accepts-inherit.html
-MISSING-LINK: css/css-typed-om/styleMap-update-function.html
 SUPPORT-WRONG-DIR: css/cssom/stylesheet-same-origin.css
 MISSING-LINK: css/cssom-view/DOMRectList.html
 MISSING-LINK: css/cssom-view/elementFromPoint-002.html
@@ -913,20 +764,14 @@
 MISSING-LINK: css/cssom-view/scrollIntoView-shadow.html
 MISSING-LINK: css/cssom-view/scrollIntoView-smooth.html
 MISSING-LINK: css/cssom-view/scrollTop-display-change.html
-CSS-COLLIDING-TEST-NAME: css/cssom-view/interfaces.html
-CSS-COLLIDING-TEST-NAME: css/cssom/interfaces.html
 
 # TODO https://github.com/w3c/web-platform-tests/issues/5770
 MISSING-LINK: css/geometry/*.worker.js
 MISSING-LINK: css/filter-effects/*.any.js
 
-WEBIDL2.JS: .gitmodules
-
-# Manual test that uses console.logs for feedback
-CONSOLE: payment-request/payment-request-response-id.html
-
 # Tests that use WebKit/Blink testing APIs
 LAYOUTTESTS APIS: css/css-regions/interactivity/*
+LAYOUTTESTS APIS: resources/chromium/generic_sensor_mocks.js
 
 # Existing use of WEB-PLATFORM.TEST
 WEB-PLATFORM.TEST: clear-site-data/support/test_utils.sub.js
@@ -939,12 +784,7 @@
 WEB-PLATFORM.TEST: html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html
 WEB-PLATFORM.TEST: html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.html
 WEB-PLATFORM.TEST: html/semantics/forms/the-label-element/label-attributes.html
-WEB-PLATFORM.TEST: longtask-timing/longtask-in-childiframe-crossorigin.html
-WEB-PLATFORM.TEST: longtask-timing/longtask-in-sibling-iframe-crossorigin.html
 WEB-PLATFORM.TEST: navigation-timing/nav2_test_attributes_values.html
 WEB-PLATFORM.TEST: navigation-timing/nav2_test_instance_accessors.html
-WEB-PLATFORM.TEST: service-workers/service-worker/update-bytecheck.https.html
-WEB-PLATFORM.TEST: webdriver/tests/cookies/add_cookie.py
-WEB-PLATFORM.TEST: webdriver/tests/cookies/get_named_cookie.py
 WEB-PLATFORM.TEST: webrtc/RTCPeerConnection-getIdentityAssertion.html
 WEB-PLATFORM.TEST: webrtc/identity-helper.js
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https-expected.txt
index 62553057..8b1bae9e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https-expected.txt
@@ -1,7 +1,9 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: Feature-Policy header magnetometer 'none' disallows the top-level document.
 PASS Magnetometer: Feature-Policy header magnetometer 'none' disallows same-origin iframes.
 PASS Magnetometer: Feature-Policy header magnetometer 'none' disallows cross-origin iframes.
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'none' disallows the top-level document. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'none' disallows same-origin iframes. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'none' disallows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https.html
index 56a1cc4..6e496c1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-disabled-by-feature-policy.https.html
@@ -8,7 +8,7 @@
 <script>
 "use strict";
 
-run_fp_tests_disabled(Magnetometer);
-run_fp_tests_disabled(UncalibratedMagnetometer);
+run_fp_tests_disabled('Magnetometer');
+run_fp_tests_disabled('UncalibratedMagnetometer');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
index e948ee0..737d71a4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https-expected.txt
@@ -1,6 +1,7 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: Feature-Policy allow='magnetometer' attribute allows same-origin relocation
 PASS Magnetometer: Feature-Policy allow='magnetometer' attribute disallows cross-origin relocation
+FAIL UncalibratedMagnetometer: Feature-Policy allow='magnetometer' attribute allows same-origin relocation assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy allow='magnetometer' attribute disallows cross-origin relocation assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
index b5f6798..4b65317 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute-redirect-on-load.https.html
@@ -8,7 +8,7 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute_redirect_on_load(Magnetometer);
-run_fp_tests_enabled_by_attribute_redirect_on_load(UncalibratedMagnetometer);
+run_fp_tests_enabled_by_attribute_redirect_on_load('Magnetometer');
+run_fp_tests_enabled_by_attribute_redirect_on_load('UncalibratedMagnetometer');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https-expected.txt
index 8bf806c..411fc9c0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https-expected.txt
@@ -1,6 +1,7 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: Feature-Policy allow='magnetometer' attribute allows same-origin iframe
 PASS Magnetometer: Feature-Policy allow='magnetometer' attribute allows cross-origin iframe
+FAIL UncalibratedMagnetometer: Feature-Policy allow='magnetometer' attribute allows same-origin iframe assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy allow='magnetometer' attribute allows cross-origin iframe assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https.html
index 6cad267..4c120f6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy-attribute.https.html
@@ -8,7 +8,7 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute(Magnetometer);
-run_fp_tests_enabled_by_attribute(UncalibratedMagnetometer);
+run_fp_tests_enabled_by_attribute('Magnetometer');
+run_fp_tests_enabled_by_attribute('UncalibratedMagnetometer');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https-expected.txt
index 5613fb0..ab2f29b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https-expected.txt
@@ -1,7 +1,9 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: Feature-Policy header magnetometer * allows the top-level document.
 PASS Magnetometer: Feature-Policy header magnetometer * allows same-origin iframes.
 PASS Magnetometer: Feature-Policy header magnetometer * allows cross-origin iframes.
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer * allows the top-level document. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer * allows same-origin iframes. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer * allows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https.html
index 24e2554b..ae59834 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-by-feature-policy.https.html
@@ -8,7 +8,7 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled(Magnetometer);
-run_fp_tests_enabled(UncalibratedMagnetometer);
+run_fp_tests_enabled('Magnetometer');
+run_fp_tests_enabled('UncalibratedMagnetometer');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
index 67c6a69..c8818b0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https-expected.txt
@@ -1,7 +1,9 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: Feature-Policy header magnetometer 'self' allows the top-level document.
 PASS Magnetometer: Feature-Policy header magnetometer 'self' allows same-origin iframes.
 PASS Magnetometer: Feature-Policy header magnetometer 'self' disallows cross-origin iframes.
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'self' allows the top-level document. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'self' allows same-origin iframes. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Feature-Policy header magnetometer 'self' disallows cross-origin iframes. assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html
index 59dfef5..e621040 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer-enabled-on-self-origin-by-feature-policy.https.html
@@ -8,7 +8,7 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_on_self_origin(Magnetometer);
-run_fp_tests_enabled_on_self_origin(UncalibratedMagnetometer);
+run_fp_tests_enabled_on_self_origin('Magnetometer');
+run_fp_tests_enabled_on_self_origin('UncalibratedMagnetometer');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https-expected.txt
index 7b01f3d9b..e0cd043 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https-expected.txt
@@ -1,20 +1,31 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
-FAIL Magnetometer: Test that 'onreading' is called and sensor reading is valid assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: sensor reading is correct assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: sensor timestamp is updated when time passes assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: Test that sensor can be successfully created and its states are correct. assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Magnetometer: sensor.start() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Magnetometer: no exception is thrown when calling start() on already started sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Magnetometer: sensor.stop() returns undefined assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Magnetometer: no exception is thrown when calling stop() on already stopped sensor assert_equals: Expected activate event, but got error event instead expected "activate" but got "error"
-FAIL Magnetometer: Test that fresh reading is fetched on start() assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: sensor readings can not be fired on the background tab assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: frequency hint works promise_test: Unhandled rejection with value: object "[object SensorErrorEvent]"
-FAIL Magnetometer: sensor receives suspend / resume notifications when  cross-origin subframe is focused assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: throw 'NotSupportedError' for an unsupported sensor option assert_throws: function "() => { new sensorType({invalid: 1}) }" did not throw
+PASS Magnetometer: Test that 'onreading' is called and sensor reading is valid
+PASS Magnetometer: sensor reading is correct
+PASS Magnetometer: sensor timestamp is updated when time passes
+PASS Magnetometer: Test that sensor can be successfully created and its states are correct.
+PASS Magnetometer: sensor.start() returns undefined
+PASS Magnetometer: no exception is thrown when calling start() on already started sensor
+PASS Magnetometer: sensor.stop() returns undefined
+PASS Magnetometer: no exception is thrown when calling stop() on already stopped sensor
+PASS Magnetometer: Test that fresh reading is fetched on start()
+PASS Magnetometer: frequency hint works
+PASS Magnetometer: sensor receives suspend / resume notifications when  cross-origin subframe is focused
 FAIL Magnetometer: throw 'TypeError' if frequency is invalid assert_throws: when freq is undefined function "() => { new sensorType({frequency: freq}) }" did not throw
-FAIL Magnetometer: sensor reading is correct when options.referenceFrame is 'screen' assert_equals: Expected reading event, but got error event instead expected "reading" but got "error"
-FAIL Magnetometer: throw 'TypeError' if referenceFrame is not one of enumeration values assert_throws: when refFrame is undefined function "() => { new sensorType({referenceFrame: refFrame}) }" did not throw
+PASS Magnetometer: sensor reading is correct when options.referenceFrame is 'screen'
+PASS Magnetometer: throw 'TypeError' if referenceFrame is not one of enumeration values
+FAIL UncalibratedMagnetometer: Test that 'onreading' is called and sensor reading is valid assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor reading is correct assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor timestamp is updated when time passes assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Test that sensor can be successfully created and its states are correct. assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor.start() returns undefined assert_true: expected true got false
+FAIL UncalibratedMagnetometer: no exception is thrown when calling start() on already started sensor assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor.stop() returns undefined assert_true: expected true got false
+FAIL UncalibratedMagnetometer: no exception is thrown when calling stop() on already stopped sensor assert_true: expected true got false
+FAIL UncalibratedMagnetometer: Test that fresh reading is fetched on start() assert_true: expected true got false
+FAIL UncalibratedMagnetometer: frequency hint works assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor receives suspend / resume notifications when  cross-origin subframe is focused assert_true: expected true got false
+FAIL UncalibratedMagnetometer: throw 'TypeError' if frequency is invalid assert_true: expected true got false
+FAIL UncalibratedMagnetometer: sensor reading is correct when options.referenceFrame is 'screen' assert_true: expected true got false
+FAIL UncalibratedMagnetometer: throw 'TypeError' if referenceFrame is not one of enumeration values assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https.html
index a3cefc2..661ea68 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer.https.html
@@ -9,7 +9,7 @@
 <div id="log"></div>
 <script>
 
-runGenericSensorTests(Magnetometer);
-runGenericSensorTests(UncalibratedMagnetometer);
+runGenericSensorTests('Magnetometer');
+runGenericSensorTests('UncalibratedMagnetometer');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https-expected.txt
index 163ea371..912aa34c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: UncalibratedMagnetometer is not defined
 PASS Magnetometer: 'onerror' event is fired when sensor is not supported
+FAIL UncalibratedMagnetometer: 'onerror' event is fired when sensor is not supported assert_true: expected true got false
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https.html
index 04b9877..dbdcf1d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/magnetometer/Magnetometer_onerror-manual.https.html
@@ -15,7 +15,7 @@
 </ol>
 <script>
 
-runGenericSensorOnerror(Magnetometer);
-runGenericSensorOnerror(UncalibratedMagnetometer);
+runGenericSensorOnerror('Magnetometer');
+runGenericSensorOnerror('UncalibratedMagnetometer');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html
index ccefe2e..7f42571 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-disabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_disabled(AbsoluteOrientationSensor);
+run_fp_tests_disabled('AbsoluteOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
index 6aed47d..ab34f5b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute_redirect_on_load(AbsoluteOrientationSensor);
+run_fp_tests_enabled_by_attribute_redirect_on_load('AbsoluteOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute.https.html
index 51bbba1..cb23e8b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy-attribute.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute(AbsoluteOrientationSensor);
+run_fp_tests_enabled_by_attribute('AbsoluteOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html
index c0b4cc8e..cc09eea 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled(AbsoluteOrientationSensor);
+run_fp_tests_enabled('AbsoluteOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
index 9ce51ab..62e21be 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_on_self_origin(AbsoluteOrientationSensor);
+run_fp_tests_enabled_on_self_origin('AbsoluteOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor.https.html
new file mode 100644
index 0000000..c476047f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/AbsoluteOrientationSensor.https.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OrientationSensor Test</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://w3c.github.io/orientation-sensor/">
+<link rel="help" href="https://w3c.github.io/sensors/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/generic-sensor/generic-sensor-tests.js"></script>
+<script src="/orientation-sensor/orientation-sensor-tests.js"></script>
+<div id="log"></div>
+
+<script>
+runOrienationSensorTests('AbsoluteOrientationSensor');
+runGenericSensorTests('AbsoluteOrientationSensor');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor_onerror-manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor_onerror-manual.https.html
index d70ea304..415a63c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor_onerror-manual.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor_onerror-manual.https.html
@@ -16,7 +16,7 @@
 </ol>
 <script>
 
-runGenericSensorOnerror(AbsoluteOrientationSensor);
-runGenericSensorOnerror(RelativeOrientationSensor);
+runGenericSensorOnerror('AbsoluteOrientationSensor');
+runGenericSensorOnerror('RelativeOrientationSensor');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html
index 9296af8..d97d40b6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-disabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_disabled(RelativeOrientationSensor);
+run_fp_tests_disabled('RelativeOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
index 01b45b0..3a6173a2 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute-redirect-on-load.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute_redirect_on_load(RelativeOrientationSensor);
+run_fp_tests_enabled_by_attribute_redirect_on_load('RelativeOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute.https.html
index 1bf4119..e8d3002 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy-attribute.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_by_attribute(RelativeOrientationSensor);
+run_fp_tests_enabled_by_attribute('RelativeOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html
index 7ea2fb5..d9f38f2d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled(RelativeOrientationSensor);
+run_fp_tests_enabled('RelativeOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
index 6d677bf0..d63bf4e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor-enabled-on-self-origin-by-feature-policy.https.html
@@ -8,6 +8,6 @@
 <script>
 "use strict";
 
-run_fp_tests_enabled_on_self_origin(RelativeOrientationSensor);
+run_fp_tests_enabled_on_self_origin('RelativeOrientationSensor');
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor.https.html
new file mode 100644
index 0000000..9521a15
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/RelativeOrientationSensor.https.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>OrientationSensor Test</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://w3c.github.io/orientation-sensor/">
+<link rel="help" href="https://w3c.github.io/sensors/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/generic-sensor/generic-sensor-tests.js"></script>
+<script src="/orientation-sensor/orientation-sensor-tests.js"></script>
+<div id="log"></div>
+
+<script>
+runOrienationSensorTests('RelativeOrientationSensor');
+runGenericSensorTests('RelativeOrientationSensor');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor.https.html b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/orientation-sensor-tests.js
similarity index 63%
rename from third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor.https.html
rename to third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/orientation-sensor-tests.js
index 5e728a6..74e360a0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/OrientationSensor.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/orientation-sensor/orientation-sensor-tests.js
@@ -1,16 +1,3 @@
- <!DOCTYPE html>
-<meta charset="utf-8">
-<title>OrientationSensor Test</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<link rel="help" href="https://w3c.github.io/orientation-sensor/">
-<link rel="help" href="https://w3c.github.io/sensors/">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/generic-sensor/generic-sensor-tests.js"></script>
-<div id="log"></div>
-
-<script>
-
 //IEEE 754: single precision retricts to 7 decimal digits
 const float_precision = 1e-7;
 
@@ -80,23 +67,17 @@
   sensor.stop();
 }
 
-promise_test(t => {
-  return checkQuaternion(t, AbsoluteOrientationSensor);
-}, "Test AbsoluteOrientationSensor.quaternion return a four-element FrozenArray.");
+function runOrienationSensorTests(sensorName) {
+  const sensorType = self[sensorName];
 
-promise_test(t => {
-  return checkQuaternion(t, RelativeOrientationSensor);
-}, "Test RelativeOrientationSensor.quaternion return a four-element FrozenArray.");
+  sensor_test(async t => {
+    assert_true(sensorName in self);
+    return checkQuaternion(t, sensorType);
+  }, `${sensorName}.quaternion return a four-element FrozenArray.`);
 
-promise_test(t => {
-  return checkPopulateMatrix(t, AbsoluteOrientationSensor);
-}, "Test AbsoluteOrientationSensor.populateMatrix() method works correctly.");
+  sensor_test(async t => {
+    assert_true(sensorName in self);
+    return checkPopulateMatrix(t, sensorType);
+  }, `${sensorName}.populateMatrix() method works correctly.`);
+}
 
-promise_test(t => {
-  return checkPopulateMatrix(t, RelativeOrientationSensor);
-}, "Test RelativeOrientationSensor.populateMatrix() method works correctly.");
-
-runGenericSensorTests(AbsoluteOrientationSensor);
-runGenericSensorTests(RelativeOrientationSensor);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-not-exposed.https.worker.js b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-not-exposed.https.worker.js
index df20f801..e5576e67 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-not-exposed.https.worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-not-exposed.https.worker.js
@@ -1,11 +1,7 @@
 importScripts("/resources/testharness.js");
 
-test(function() {
+test(() => {
   assert_true(isSecureContext);
   assert_false('PaymentRequest' in self);
-  assert_false('PaymentRequestUpdateEvent' in self);
-  assert_false('PaymentResponse' in self);
-  assert_false('PaymentAddress' in self);
 }, "PaymentRequest constructor must not be exposed in worker global scope");
-
 done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js
new file mode 100644
index 0000000..ff3a051
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js
@@ -0,0 +1,226 @@
+'use strict';
+
+var GenericSensorTest = (() => {
+  // Class that mocks Sensor interface defined in
+  // https://cs.chromium.org/chromium/src/services/device/public/mojom/sensor.mojom
+  class MockSensor {
+    constructor(sensorRequest, handle, offset, size, reportingMode) {
+      this.client_ = null;
+      this.reportingMode_ = reportingMode;
+      this.sensorReadingTimerId_ = null;
+      this.requestedFrequencies_ = [];
+      let rv = handle.mapBuffer(offset, size);
+      assert_equals(rv.result, Mojo.RESULT_OK, "Failed to map shared buffer");
+      this.buffer_ = new Float64Array(rv.buffer);
+      this.buffer_.fill(0);
+      this.binding_ = new mojo.Binding(device.mojom.Sensor, this,
+                                       sensorRequest);
+      this.binding_.setConnectionErrorHandler(() => {
+        this.reset();
+      });
+    }
+
+    getDefaultConfiguration() {
+      return Promise.resolve({frequency: 5});
+    }
+
+    addConfiguration(configuration) {
+      assert_not_equals(configuration, null, "Invalid sensor configuration.");
+
+      this.requestedFrequencies_.push(configuration.frequency);
+      // Sort using descending order.
+      this.requestedFrequencies_.sort(
+          (first, second) => { return second - first });
+
+      this.startReading();
+
+      return Promise.resolve({success: true});
+    }
+
+    removeConfiguration(configuration) {
+      let index = this.requestedFrequencies_.indexOf(configuration.frequency);
+      if (index == -1)
+        return;
+
+      this.requestedFrequencies_.splice(index, 1);
+
+      if (this.isReading) {
+        this.stopReading();
+        if (this.requestedFrequencies_.length !== 0)
+          this.startReading();
+      }
+    }
+
+    suspend() {
+      this.stopReading();
+    }
+
+    resume() {
+      this.startReading();
+    }
+
+    reset() {
+      this.stopReading();
+      this.requestedFrequencies_ = [];
+      this.buffer_.fill(0);
+      this.binding_.close();
+    }
+
+    startReading() {
+      if (this.isReading) {
+        console.warn("Sensor reading is already started.");
+        return;
+      }
+
+      if (this.requestedFrequencies_.length == 0) {
+        console.warn("Sensor reading cannot be started as" +
+                     "there are no configurations added.");
+        return;
+      }
+
+      const maxFrequencyHz = this.requestedFrequencies_[0];
+      const timeoutMs = (1 / maxFrequencyHz) * 1000;
+      this.sensorReadingTimerId_ = window.setInterval(() => {
+        // For all tests sensor reading should have monotonically
+        // increasing timestamp in seconds.
+        this.buffer_[1] = window.performance.now() * 0.001;
+        if (this.reportingMode_ === device.mojom.ReportingMode.ON_CHANGE) {
+          this.client_.sensorReadingChanged();
+        }
+      }, timeoutMs);
+    }
+
+    stopReading() {
+      if (this.isReading) {
+        window.clearInterval(this.sensorReadingTimerId_);
+        this.sensorReadingTimerId_ = null;
+      }
+    }
+
+     get isReading() {
+       this.sensorReadingTimerId_ !== null;
+     }
+  }
+
+  // Class that mocks SensorProvider interface defined in
+  // https://cs.chromium.org/chromium/src/services/device/public/mojom/sensor_provider.mojom
+  class MockSensorProvider {
+    constructor() {
+      this.readingSizeInBytes_ =
+          device.mojom.SensorInitParams.kReadBufferSizeForTests;
+      this.sharedBufferSizeInBytes_ = this.readingSizeInBytes_ *
+              device.mojom.SensorType.LAST;
+      let rv = Mojo.createSharedBuffer(this.sharedBufferSizeInBytes_);
+      assert_equals(rv.result, Mojo.RESULT_OK, "Failed to create buffer");
+      this.sharedBufferHandle_ = rv.handle;
+      this.activeSensor_ = null;
+      this.isContinuous_ = false;
+      this.binding_ = new mojo.Binding(device.mojom.SensorProvider, this);
+
+      this.interceptor_ = new MojoInterfaceInterceptor(
+          device.mojom.SensorProvider.name);
+      this.interceptor_.oninterfacerequest = e => {
+        this.binding_.bind(e.handle);
+        this.binding_.setConnectionErrorHandler(() => {
+          console.error("Mojo connection error");
+          this.reset();
+        });
+      };
+      this.interceptor_.start();
+    }
+
+    async getSensor(type) {
+      const offset = (device.mojom.SensorType.LAST - type) *
+                      this.readingSizeInBytes_;
+      const reportingMode = device.mojom.ReportingMode.ON_CHANGE;
+
+      let sensorPtr = new device.mojom.SensorPtr();
+      if (this.activeSensor_ == null) {
+        let mockSensor = new MockSensor(
+            mojo.makeRequest(sensorPtr), this.sharedBufferHandle_, offset,
+            this.readingSizeInBytes_, reportingMode);
+        this.activeSensor_ = mockSensor;
+        this.activeSensor_.client_ = new device.mojom.SensorClientPtr();
+      }
+
+      let rv = this.sharedBufferHandle_.duplicateBufferHandle();
+
+      assert_equals(rv.result, Mojo.RESULT_OK);
+      let maxAllowedFrequencyHz = 60;
+      if (type == device.mojom.SensorType.AMBIENT_LIGHT ||
+          type == device.mojom.SensorType.MAGNETOMETER) {
+        maxAllowedFrequencyHz = 10;
+      }
+
+      let initParams = new device.mojom.SensorInitParams({
+        sensor: sensorPtr,
+        clientRequest: mojo.makeRequest(this.activeSensor_.client_),
+        memory: rv.handle,
+        bufferOffset: offset,
+        mode: reportingMode,
+        defaultConfiguration: {frequency: 5},
+        minimumFrequency: 1,
+        maximumFrequency: maxAllowedFrequencyHz
+      });
+
+      return {result: device.mojom.SensorCreationResult.SUCCESS,
+              initParams: initParams};
+    }
+
+    async reset() {
+      if (this.activeSensor_ !== null) {
+        this.activeSensor_.reset();
+        this.activeSensor_ = null;
+      }
+      // Wait for an event loop iteration to let
+      // the pending mojo commands pass.
+      function schedule(func) {
+        return new Promise(resolve => {
+          setTimeout(() => {
+            func();
+            resolve();
+          }, 0);
+        });
+      }
+      await schedule(this.binding_.close.bind(this.binding_));
+      await schedule(this.interceptor_.stop.bind(this.interceptor_));
+    }
+  }
+
+  let testInternal = {
+    initialized: false,
+    sensorProvider: null
+  }
+
+  class GenericSensorTestChromium {
+    constructor() {
+      Object.freeze(this); // Make it immutable.
+    }
+
+    initialize() {
+      if (testInternal.initialized)
+        throw new Error('Call reset() before initialize().');
+
+      if (testRunner) { // Grant sensor permissions for Chromium testrunner.
+        ['accelerometer', 'gyroscope',
+         'magnetometer', 'ambient-light-sensor'].forEach((entry) => {
+          testRunner.setPermission(entry, 'granted',
+                                   location.origin, location.origin);
+        });
+      }
+
+      testInternal.sensorProvider = new MockSensorProvider;
+      testInternal.initialized = true;
+    }
+    // Resets state of sensor mocks between test runs.
+    async reset() {
+      if (!testInternal.initialized)
+        throw new Error('Call initialize() before reset().');
+      await testInternal.sensorProvider.reset();
+      testInternal.sensorProvider = null;
+      testInternal.initialized = false;
+    }
+  }
+
+  return GenericSensorTestChromium;
+})();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js.headers b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js.headers
new file mode 100644
index 0000000..6805c323
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/generic_sensor_mocks.js.headers
@@ -0,0 +1 @@
+Content-Type: text/javascript; charset=utf-8
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor.mojom.js
new file mode 100644
index 0000000..9e75aed
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor.mojom.js
@@ -0,0 +1,1072 @@
+// Copyright 2014 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.
+
+'use strict';
+
+(function() {
+  var mojomId = 'services/device/public/mojom/sensor.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+  var exports = mojo.internal.exposeNamespace('device.mojom');
+
+
+  var SensorType = {};
+  SensorType.FIRST = 1;
+  SensorType.AMBIENT_LIGHT = SensorType.FIRST;
+  SensorType.PROXIMITY = SensorType.AMBIENT_LIGHT + 1;
+  SensorType.ACCELEROMETER = SensorType.PROXIMITY + 1;
+  SensorType.LINEAR_ACCELERATION = SensorType.ACCELEROMETER + 1;
+  SensorType.GYROSCOPE = SensorType.LINEAR_ACCELERATION + 1;
+  SensorType.MAGNETOMETER = SensorType.GYROSCOPE + 1;
+  SensorType.PRESSURE = SensorType.MAGNETOMETER + 1;
+  SensorType.ABSOLUTE_ORIENTATION_EULER_ANGLES = SensorType.PRESSURE + 1;
+  SensorType.ABSOLUTE_ORIENTATION_QUATERNION = SensorType.ABSOLUTE_ORIENTATION_EULER_ANGLES + 1;
+  SensorType.RELATIVE_ORIENTATION_EULER_ANGLES = SensorType.ABSOLUTE_ORIENTATION_QUATERNION + 1;
+  SensorType.RELATIVE_ORIENTATION_QUATERNION = SensorType.RELATIVE_ORIENTATION_EULER_ANGLES + 1;
+  SensorType.LAST = SensorType.RELATIVE_ORIENTATION_QUATERNION;
+
+  SensorType.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+      return true;
+    }
+    return false;
+  };
+
+  SensorType.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var ReportingMode = {};
+  ReportingMode.ON_CHANGE = 0;
+  ReportingMode.CONTINUOUS = ReportingMode.ON_CHANGE + 1;
+
+  ReportingMode.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+      return true;
+    }
+    return false;
+  };
+
+  ReportingMode.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+
+  function SensorConfiguration(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorConfiguration.prototype.initDefaults_ = function() {
+    this.frequency = 0;
+  };
+  SensorConfiguration.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorConfiguration.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  SensorConfiguration.encodedSize = codec.kStructHeaderSize + 8;
+
+  SensorConfiguration.decode = function(decoder) {
+    var packed;
+    var val = new SensorConfiguration();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.frequency = decoder.decodeStruct(codec.Double);
+    return val;
+  };
+
+  SensorConfiguration.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorConfiguration.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Double, val.frequency);
+  };
+  function Sensor_GetDefaultConfiguration_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_GetDefaultConfiguration_Params.prototype.initDefaults_ = function() {
+  };
+  Sensor_GetDefaultConfiguration_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_GetDefaultConfiguration_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_GetDefaultConfiguration_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  Sensor_GetDefaultConfiguration_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_GetDefaultConfiguration_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  Sensor_GetDefaultConfiguration_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_GetDefaultConfiguration_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function Sensor_GetDefaultConfiguration_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_GetDefaultConfiguration_ResponseParams.prototype.initDefaults_ = function() {
+    this.configuration = null;
+  };
+  Sensor_GetDefaultConfiguration_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_GetDefaultConfiguration_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate Sensor_GetDefaultConfiguration_ResponseParams.configuration
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, SensorConfiguration, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_GetDefaultConfiguration_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  Sensor_GetDefaultConfiguration_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_GetDefaultConfiguration_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.configuration = decoder.decodeStructPointer(SensorConfiguration);
+    return val;
+  };
+
+  Sensor_GetDefaultConfiguration_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_GetDefaultConfiguration_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(SensorConfiguration, val.configuration);
+  };
+  function Sensor_AddConfiguration_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_AddConfiguration_Params.prototype.initDefaults_ = function() {
+    this.configuration = null;
+  };
+  Sensor_AddConfiguration_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_AddConfiguration_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate Sensor_AddConfiguration_Params.configuration
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, SensorConfiguration, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_AddConfiguration_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  Sensor_AddConfiguration_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_AddConfiguration_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.configuration = decoder.decodeStructPointer(SensorConfiguration);
+    return val;
+  };
+
+  Sensor_AddConfiguration_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_AddConfiguration_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(SensorConfiguration, val.configuration);
+  };
+  function Sensor_AddConfiguration_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_AddConfiguration_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  Sensor_AddConfiguration_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_AddConfiguration_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_AddConfiguration_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  Sensor_AddConfiguration_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_AddConfiguration_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  Sensor_AddConfiguration_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_AddConfiguration_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function Sensor_RemoveConfiguration_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_RemoveConfiguration_Params.prototype.initDefaults_ = function() {
+    this.configuration = null;
+  };
+  Sensor_RemoveConfiguration_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_RemoveConfiguration_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate Sensor_RemoveConfiguration_Params.configuration
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, SensorConfiguration, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_RemoveConfiguration_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  Sensor_RemoveConfiguration_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_RemoveConfiguration_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.configuration = decoder.decodeStructPointer(SensorConfiguration);
+    return val;
+  };
+
+  Sensor_RemoveConfiguration_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_RemoveConfiguration_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(SensorConfiguration, val.configuration);
+  };
+  function Sensor_Suspend_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_Suspend_Params.prototype.initDefaults_ = function() {
+  };
+  Sensor_Suspend_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_Suspend_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_Suspend_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  Sensor_Suspend_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_Suspend_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  Sensor_Suspend_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_Suspend_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function Sensor_Resume_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_Resume_Params.prototype.initDefaults_ = function() {
+  };
+  Sensor_Resume_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_Resume_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_Resume_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  Sensor_Resume_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_Resume_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  Sensor_Resume_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_Resume_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function Sensor_ConfigureReadingChangeNotifications_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  Sensor_ConfigureReadingChangeNotifications_Params.prototype.initDefaults_ = function() {
+    this.enabled = false;
+  };
+  Sensor_ConfigureReadingChangeNotifications_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  Sensor_ConfigureReadingChangeNotifications_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  Sensor_ConfigureReadingChangeNotifications_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  Sensor_ConfigureReadingChangeNotifications_Params.decode = function(decoder) {
+    var packed;
+    var val = new Sensor_ConfigureReadingChangeNotifications_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.enabled = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  Sensor_ConfigureReadingChangeNotifications_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(Sensor_ConfigureReadingChangeNotifications_Params.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.enabled & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function SensorClient_RaiseError_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorClient_RaiseError_Params.prototype.initDefaults_ = function() {
+  };
+  SensorClient_RaiseError_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorClient_RaiseError_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  SensorClient_RaiseError_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  SensorClient_RaiseError_Params.decode = function(decoder) {
+    var packed;
+    var val = new SensorClient_RaiseError_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  SensorClient_RaiseError_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorClient_RaiseError_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function SensorClient_SensorReadingChanged_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorClient_SensorReadingChanged_Params.prototype.initDefaults_ = function() {
+  };
+  SensorClient_SensorReadingChanged_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorClient_SensorReadingChanged_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  SensorClient_SensorReadingChanged_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  SensorClient_SensorReadingChanged_Params.decode = function(decoder) {
+    var packed;
+    var val = new SensorClient_SensorReadingChanged_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  SensorClient_SensorReadingChanged_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorClient_SensorReadingChanged_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  var kSensor_GetDefaultConfiguration_Name = 0;
+  var kSensor_AddConfiguration_Name = 1;
+  var kSensor_RemoveConfiguration_Name = 2;
+  var kSensor_Suspend_Name = 3;
+  var kSensor_Resume_Name = 4;
+  var kSensor_ConfigureReadingChangeNotifications_Name = 5;
+
+  function SensorPtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(Sensor,
+                                                   handleOrPtrInfo);
+  }
+
+  function SensorAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        Sensor, associatedInterfacePtrInfo);
+  }
+
+  SensorAssociatedPtr.prototype =
+      Object.create(SensorPtr.prototype);
+  SensorAssociatedPtr.prototype.constructor =
+      SensorAssociatedPtr;
+
+  function SensorProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  SensorPtr.prototype.getDefaultConfiguration = function() {
+    return SensorProxy.prototype.getDefaultConfiguration
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.getDefaultConfiguration = function() {
+    var params = new Sensor_GetDefaultConfiguration_Params();
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kSensor_GetDefaultConfiguration_Name,
+          codec.align(Sensor_GetDefaultConfiguration_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(Sensor_GetDefaultConfiguration_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(Sensor_GetDefaultConfiguration_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  SensorPtr.prototype.addConfiguration = function() {
+    return SensorProxy.prototype.addConfiguration
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.addConfiguration = function(configuration) {
+    var params = new Sensor_AddConfiguration_Params();
+    params.configuration = configuration;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kSensor_AddConfiguration_Name,
+          codec.align(Sensor_AddConfiguration_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(Sensor_AddConfiguration_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(Sensor_AddConfiguration_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  SensorPtr.prototype.removeConfiguration = function() {
+    return SensorProxy.prototype.removeConfiguration
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.removeConfiguration = function(configuration) {
+    var params = new Sensor_RemoveConfiguration_Params();
+    params.configuration = configuration;
+    var builder = new codec.MessageV0Builder(
+        kSensor_RemoveConfiguration_Name,
+        codec.align(Sensor_RemoveConfiguration_Params.encodedSize));
+    builder.encodeStruct(Sensor_RemoveConfiguration_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  SensorPtr.prototype.suspend = function() {
+    return SensorProxy.prototype.suspend
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.suspend = function() {
+    var params = new Sensor_Suspend_Params();
+    var builder = new codec.MessageV0Builder(
+        kSensor_Suspend_Name,
+        codec.align(Sensor_Suspend_Params.encodedSize));
+    builder.encodeStruct(Sensor_Suspend_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  SensorPtr.prototype.resume = function() {
+    return SensorProxy.prototype.resume
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.resume = function() {
+    var params = new Sensor_Resume_Params();
+    var builder = new codec.MessageV0Builder(
+        kSensor_Resume_Name,
+        codec.align(Sensor_Resume_Params.encodedSize));
+    builder.encodeStruct(Sensor_Resume_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  SensorPtr.prototype.configureReadingChangeNotifications = function() {
+    return SensorProxy.prototype.configureReadingChangeNotifications
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProxy.prototype.configureReadingChangeNotifications = function(enabled) {
+    var params = new Sensor_ConfigureReadingChangeNotifications_Params();
+    params.enabled = enabled;
+    var builder = new codec.MessageV0Builder(
+        kSensor_ConfigureReadingChangeNotifications_Name,
+        codec.align(Sensor_ConfigureReadingChangeNotifications_Params.encodedSize));
+    builder.encodeStruct(Sensor_ConfigureReadingChangeNotifications_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+
+  function SensorStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  SensorStub.prototype.getDefaultConfiguration = function() {
+    return this.delegate_ && this.delegate_.getDefaultConfiguration && this.delegate_.getDefaultConfiguration();
+  }
+  SensorStub.prototype.addConfiguration = function(configuration) {
+    return this.delegate_ && this.delegate_.addConfiguration && this.delegate_.addConfiguration(configuration);
+  }
+  SensorStub.prototype.removeConfiguration = function(configuration) {
+    return this.delegate_ && this.delegate_.removeConfiguration && this.delegate_.removeConfiguration(configuration);
+  }
+  SensorStub.prototype.suspend = function() {
+    return this.delegate_ && this.delegate_.suspend && this.delegate_.suspend();
+  }
+  SensorStub.prototype.resume = function() {
+    return this.delegate_ && this.delegate_.resume && this.delegate_.resume();
+  }
+  SensorStub.prototype.configureReadingChangeNotifications = function(enabled) {
+    return this.delegate_ && this.delegate_.configureReadingChangeNotifications && this.delegate_.configureReadingChangeNotifications(enabled);
+  }
+
+  SensorStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kSensor_RemoveConfiguration_Name:
+      var params = reader.decodeStruct(Sensor_RemoveConfiguration_Params);
+      this.removeConfiguration(params.configuration);
+      return true;
+    case kSensor_Suspend_Name:
+      var params = reader.decodeStruct(Sensor_Suspend_Params);
+      this.suspend();
+      return true;
+    case kSensor_Resume_Name:
+      var params = reader.decodeStruct(Sensor_Resume_Params);
+      this.resume();
+      return true;
+    case kSensor_ConfigureReadingChangeNotifications_Name:
+      var params = reader.decodeStruct(Sensor_ConfigureReadingChangeNotifications_Params);
+      this.configureReadingChangeNotifications(params.enabled);
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  SensorStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kSensor_GetDefaultConfiguration_Name:
+      var params = reader.decodeStruct(Sensor_GetDefaultConfiguration_Params);
+      this.getDefaultConfiguration().then(function(response) {
+        var responseParams =
+            new Sensor_GetDefaultConfiguration_ResponseParams();
+        responseParams.configuration = response.configuration;
+        var builder = new codec.MessageV1Builder(
+            kSensor_GetDefaultConfiguration_Name,
+            codec.align(Sensor_GetDefaultConfiguration_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(Sensor_GetDefaultConfiguration_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kSensor_AddConfiguration_Name:
+      var params = reader.decodeStruct(Sensor_AddConfiguration_Params);
+      this.addConfiguration(params.configuration).then(function(response) {
+        var responseParams =
+            new Sensor_AddConfiguration_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kSensor_AddConfiguration_Name,
+            codec.align(Sensor_AddConfiguration_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(Sensor_AddConfiguration_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  function validateSensorRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kSensor_GetDefaultConfiguration_Name:
+        if (message.expectsResponse())
+          paramsClass = Sensor_GetDefaultConfiguration_Params;
+      break;
+      case kSensor_AddConfiguration_Name:
+        if (message.expectsResponse())
+          paramsClass = Sensor_AddConfiguration_Params;
+      break;
+      case kSensor_RemoveConfiguration_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = Sensor_RemoveConfiguration_Params;
+      break;
+      case kSensor_Suspend_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = Sensor_Suspend_Params;
+      break;
+      case kSensor_Resume_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = Sensor_Resume_Params;
+      break;
+      case kSensor_ConfigureReadingChangeNotifications_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = Sensor_ConfigureReadingChangeNotifications_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateSensorResponse(messageValidator) {
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+      case kSensor_GetDefaultConfiguration_Name:
+        if (message.isResponse())
+          paramsClass = Sensor_GetDefaultConfiguration_ResponseParams;
+        break;
+      case kSensor_AddConfiguration_Name:
+        if (message.isResponse())
+          paramsClass = Sensor_AddConfiguration_ResponseParams;
+        break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  var Sensor = {
+    name: 'device::mojom::Sensor',
+    kVersion: 0,
+    ptrClass: SensorPtr,
+    proxyClass: SensorProxy,
+    stubClass: SensorStub,
+    validateRequest: validateSensorRequest,
+    validateResponse: validateSensorResponse,
+  };
+  SensorStub.prototype.validator = validateSensorRequest;
+  SensorProxy.prototype.validator = validateSensorResponse;
+  var kSensorClient_RaiseError_Name = 0;
+  var kSensorClient_SensorReadingChanged_Name = 1;
+
+  function SensorClientPtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(SensorClient,
+                                                   handleOrPtrInfo);
+  }
+
+  function SensorClientAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        SensorClient, associatedInterfacePtrInfo);
+  }
+
+  SensorClientAssociatedPtr.prototype =
+      Object.create(SensorClientPtr.prototype);
+  SensorClientAssociatedPtr.prototype.constructor =
+      SensorClientAssociatedPtr;
+
+  function SensorClientProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  SensorClientPtr.prototype.raiseError = function() {
+    return SensorClientProxy.prototype.raiseError
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorClientProxy.prototype.raiseError = function() {
+    var params = new SensorClient_RaiseError_Params();
+    var builder = new codec.MessageV0Builder(
+        kSensorClient_RaiseError_Name,
+        codec.align(SensorClient_RaiseError_Params.encodedSize));
+    builder.encodeStruct(SensorClient_RaiseError_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  SensorClientPtr.prototype.sensorReadingChanged = function() {
+    return SensorClientProxy.prototype.sensorReadingChanged
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorClientProxy.prototype.sensorReadingChanged = function() {
+    var params = new SensorClient_SensorReadingChanged_Params();
+    var builder = new codec.MessageV0Builder(
+        kSensorClient_SensorReadingChanged_Name,
+        codec.align(SensorClient_SensorReadingChanged_Params.encodedSize));
+    builder.encodeStruct(SensorClient_SensorReadingChanged_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+
+  function SensorClientStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  SensorClientStub.prototype.raiseError = function() {
+    return this.delegate_ && this.delegate_.raiseError && this.delegate_.raiseError();
+  }
+  SensorClientStub.prototype.sensorReadingChanged = function() {
+    return this.delegate_ && this.delegate_.sensorReadingChanged && this.delegate_.sensorReadingChanged();
+  }
+
+  SensorClientStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kSensorClient_RaiseError_Name:
+      var params = reader.decodeStruct(SensorClient_RaiseError_Params);
+      this.raiseError();
+      return true;
+    case kSensorClient_SensorReadingChanged_Name:
+      var params = reader.decodeStruct(SensorClient_SensorReadingChanged_Params);
+      this.sensorReadingChanged();
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  SensorClientStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    default:
+      return false;
+    }
+  };
+
+  function validateSensorClientRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kSensorClient_RaiseError_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = SensorClient_RaiseError_Params;
+      break;
+      case kSensorClient_SensorReadingChanged_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = SensorClient_SensorReadingChanged_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateSensorClientResponse(messageValidator) {
+    return validator.validationError.NONE;
+  }
+
+  var SensorClient = {
+    name: 'device::mojom::SensorClient',
+    kVersion: 0,
+    ptrClass: SensorClientPtr,
+    proxyClass: SensorClientProxy,
+    stubClass: SensorClientStub,
+    validateRequest: validateSensorClientRequest,
+    validateResponse: null,
+  };
+  SensorClientStub.prototype.validator = validateSensorClientRequest;
+  SensorClientProxy.prototype.validator = null;
+  exports.SensorType = SensorType;
+  exports.ReportingMode = ReportingMode;
+  exports.SensorConfiguration = SensorConfiguration;
+  exports.Sensor = Sensor;
+  exports.SensorPtr = SensorPtr;
+  exports.SensorAssociatedPtr = SensorAssociatedPtr;
+  exports.SensorClient = SensorClient;
+  exports.SensorClientPtr = SensorClientPtr;
+  exports.SensorClientAssociatedPtr = SensorClientAssociatedPtr;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor_provider.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor_provider.mojom.js
new file mode 100644
index 0000000..2fac8b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/sensor_provider.mojom.js
@@ -0,0 +1,429 @@
+// Copyright 2014 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.
+
+'use strict';
+
+(function() {
+  var mojomId = 'services/device/public/mojom/sensor_provider.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+  var exports = mojo.internal.exposeNamespace('device.mojom');
+  var sensor$ =
+      mojo.internal.exposeNamespace('device.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'services/device/public/mojom/sensor.mojom', 'sensor.mojom.js');
+  }
+
+
+  var SensorCreationResult = {};
+  SensorCreationResult.SUCCESS = 0;
+  SensorCreationResult.ERROR_NOT_AVAILABLE = SensorCreationResult.SUCCESS + 1;
+  SensorCreationResult.ERROR_NOT_ALLOWED = SensorCreationResult.ERROR_NOT_AVAILABLE + 1;
+
+  SensorCreationResult.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    }
+    return false;
+  };
+
+  SensorCreationResult.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+
+  function SensorInitParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorInitParams.kReadBufferSizeForTests = 48;
+  SensorInitParams.prototype.initDefaults_ = function() {
+    this.sensor = new sensor$.SensorPtr();
+    this.clientRequest = new bindings.InterfaceRequest();
+    this.memory = null;
+    this.bufferOffset = 0;
+    this.mode = 0;
+    this.defaultConfiguration = null;
+    this.maximumFrequency = 0;
+    this.minimumFrequency = 0;
+  };
+  SensorInitParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorInitParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 64}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorInitParams.sensor
+    err = messageValidator.validateInterface(offset + codec.kStructHeaderSize + 0, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorInitParams.clientRequest
+    err = messageValidator.validateInterfaceRequest(offset + codec.kStructHeaderSize + 8, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorInitParams.memory
+    err = messageValidator.validateHandle(offset + codec.kStructHeaderSize + 12, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    // validate SensorInitParams.mode
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 24, sensor$.ReportingMode);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorInitParams.defaultConfiguration
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 32, sensor$.SensorConfiguration, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  SensorInitParams.encodedSize = codec.kStructHeaderSize + 56;
+
+  SensorInitParams.decode = function(decoder) {
+    var packed;
+    var val = new SensorInitParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.sensor = decoder.decodeStruct(new codec.Interface(sensor$.SensorPtr));
+    val.clientRequest = decoder.decodeStruct(codec.InterfaceRequest);
+    val.memory = decoder.decodeStruct(codec.Handle);
+    val.bufferOffset = decoder.decodeStruct(codec.Uint64);
+    val.mode = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.defaultConfiguration = decoder.decodeStructPointer(sensor$.SensorConfiguration);
+    val.maximumFrequency = decoder.decodeStruct(codec.Double);
+    val.minimumFrequency = decoder.decodeStruct(codec.Double);
+    return val;
+  };
+
+  SensorInitParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorInitParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(new codec.Interface(sensor$.SensorPtr), val.sensor);
+    encoder.encodeStruct(codec.InterfaceRequest, val.clientRequest);
+    encoder.encodeStruct(codec.Handle, val.memory);
+    encoder.encodeStruct(codec.Uint64, val.bufferOffset);
+    encoder.encodeStruct(codec.Int32, val.mode);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStructPointer(sensor$.SensorConfiguration, val.defaultConfiguration);
+    encoder.encodeStruct(codec.Double, val.maximumFrequency);
+    encoder.encodeStruct(codec.Double, val.minimumFrequency);
+  };
+  function SensorProvider_GetSensor_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorProvider_GetSensor_Params.prototype.initDefaults_ = function() {
+    this.type = 0;
+  };
+  SensorProvider_GetSensor_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorProvider_GetSensor_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorProvider_GetSensor_Params.type
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, sensor$.SensorType);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  SensorProvider_GetSensor_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  SensorProvider_GetSensor_Params.decode = function(decoder) {
+    var packed;
+    var val = new SensorProvider_GetSensor_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.type = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  SensorProvider_GetSensor_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorProvider_GetSensor_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.type);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function SensorProvider_GetSensor_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  SensorProvider_GetSensor_ResponseParams.prototype.initDefaults_ = function() {
+    this.result = 0;
+    this.initParams = null;
+  };
+  SensorProvider_GetSensor_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  SensorProvider_GetSensor_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorProvider_GetSensor_ResponseParams.result
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, SensorCreationResult);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate SensorProvider_GetSensor_ResponseParams.initParams
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, SensorInitParams, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  SensorProvider_GetSensor_ResponseParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  SensorProvider_GetSensor_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new SensorProvider_GetSensor_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.result = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.initParams = decoder.decodeStructPointer(SensorInitParams);
+    return val;
+  };
+
+  SensorProvider_GetSensor_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(SensorProvider_GetSensor_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.result);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStructPointer(SensorInitParams, val.initParams);
+  };
+  var kSensorProvider_GetSensor_Name = 0;
+
+  function SensorProviderPtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(SensorProvider,
+                                                   handleOrPtrInfo);
+  }
+
+  function SensorProviderAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        SensorProvider, associatedInterfacePtrInfo);
+  }
+
+  SensorProviderAssociatedPtr.prototype =
+      Object.create(SensorProviderPtr.prototype);
+  SensorProviderAssociatedPtr.prototype.constructor =
+      SensorProviderAssociatedPtr;
+
+  function SensorProviderProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  SensorProviderPtr.prototype.getSensor = function() {
+    return SensorProviderProxy.prototype.getSensor
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  SensorProviderProxy.prototype.getSensor = function(type) {
+    var params = new SensorProvider_GetSensor_Params();
+    params.type = type;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kSensorProvider_GetSensor_Name,
+          codec.align(SensorProvider_GetSensor_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(SensorProvider_GetSensor_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(SensorProvider_GetSensor_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+
+  function SensorProviderStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  SensorProviderStub.prototype.getSensor = function(type) {
+    return this.delegate_ && this.delegate_.getSensor && this.delegate_.getSensor(type);
+  }
+
+  SensorProviderStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    default:
+      return false;
+    }
+  };
+
+  SensorProviderStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kSensorProvider_GetSensor_Name:
+      var params = reader.decodeStruct(SensorProvider_GetSensor_Params);
+      this.getSensor(params.type).then(function(response) {
+        var responseParams =
+            new SensorProvider_GetSensor_ResponseParams();
+        responseParams.result = response.result;
+        responseParams.initParams = response.initParams;
+        var builder = new codec.MessageV1Builder(
+            kSensorProvider_GetSensor_Name,
+            codec.align(SensorProvider_GetSensor_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(SensorProvider_GetSensor_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  function validateSensorProviderRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kSensorProvider_GetSensor_Name:
+        if (message.expectsResponse())
+          paramsClass = SensorProvider_GetSensor_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateSensorProviderResponse(messageValidator) {
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+      case kSensorProvider_GetSensor_Name:
+        if (message.isResponse())
+          paramsClass = SensorProvider_GetSensor_ResponseParams;
+        break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  var SensorProvider = {
+    name: 'device::mojom::SensorProvider',
+    kVersion: 0,
+    ptrClass: SensorProviderPtr,
+    proxyClass: SensorProviderProxy,
+    stubClass: SensorProviderStub,
+    validateRequest: validateSensorProviderRequest,
+    validateResponse: validateSensorProviderResponse,
+  };
+  SensorProviderStub.prototype.validator = validateSensorProviderRequest;
+  SensorProviderProxy.prototype.validator = validateSensorProviderResponse;
+  exports.SensorCreationResult = SensorCreationResult;
+  exports.SensorInitParams = SensorInitParams;
+  exports.SensorProvider = SensorProvider;
+  exports.SensorProviderPtr = SensorProviderPtr;
+  exports.SensorProviderAssociatedPtr = SensorProviderAssociatedPtr;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl-expected.txt
new file mode 100644
index 0000000..c75bc11
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl.html b/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl.html
new file mode 100644
index 0000000..1c1be899
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/service_worker_idl.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<head>
+    <meta charset='utf-8' />
+    <script src="/resources/testharness.js"></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script>
+      (async () => {
+        const scope = 'does/not/exist'
+
+        let registration = await navigator.serviceWorker.getRegistration(scope)
+        if (registration)
+          await registration.unregister()
+        registration = await navigator.serviceWorker.register('./sw.js', {scope})
+
+        fetch_tests_from_worker(registration.installing)
+      })()
+    </script>
+</head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/sw.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/sw.js
new file mode 100644
index 0000000..4d3b620
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/sw.js
@@ -0,0 +1,10 @@
+importScripts('/resources/testharness.js')
+
+promise_test(async (test) => {
+  return fetch('./sw.js').then(function(response) {
+    assert_not_equals(typeof performance.getEntriesByName(response.url)[0].serverTiming,
+      'undefined',
+      'An instance of `PerformanceResourceTiming` should have a `serverTiming` attribute in the Service Worker context.')
+    done()
+  })
+})
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/resource-timing-worker.js b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/resource-timing-worker.js
index c83e4851..8b3046722 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/resource-timing-worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/resource-timing-worker.js
@@ -1,5 +1,9 @@
 self.addEventListener('fetch', function(event) {
-    if (event.request.url.indexOf('dummy.js') != -1) {
-      event.respondWith(fetch('empty.js'));
-    }
-  });
+  if (event.request.url.indexOf('dummy.js') != -1) {
+    event.respondWith(new Promise(resolve => {
+      // Slightly delay the response so we ensure we get a non-zero
+      // duration.
+      setTimeout(_ => resolve(new Response('// Empty javascript')), 50);
+    }));
+  }
+});
diff --git a/third_party/WebKit/LayoutTests/external/wpt/url/setters_tests.json b/third_party/WebKit/LayoutTests/external/wpt/url/setters_tests.json
index 714bd5e7..db23d92 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/url/setters_tests.json
+++ b/third_party/WebKit/LayoutTests/external/wpt/url/setters_tests.json
@@ -720,6 +720,17 @@
             }
         },
         {
+            "comment": "Port number is removed if new port is scheme default and existing URL has a non-default port",
+            "href": "http://example.net:8080",
+            "new_value": "example.com:80",
+            "expected": {
+                "href": "http://example.com/",
+                "host": "example.com",
+                "hostname": "example.com",
+                "port": ""
+            }
+        },
+        {
             "comment": "Stuff after a / delimiter is ignored",
             "href": "http://example.net/path",
             "new_value": "example.com/stuff",
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-basic.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-basic.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html
index 4e23760..e176d61 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-basic.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html
@@ -4,10 +4,10 @@
     <title>
       realtimeanalyser-basic.html
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-scaling.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html
similarity index 93%
rename from third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-scaling.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html
index dadf683..6a0ab7d 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-scaling.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html
@@ -4,10 +4,10 @@
     <title>
       realtimeanalyser-fft-scaling.html
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <div id="description"></div>
diff --git a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-sizing.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-sizing.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html
index 0b0bb3bb..b3de37f 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Analyser/realtimeanalyser-fft-sizing.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html
@@ -4,10 +4,10 @@
     <title>
       realtimeanalyser-fft-sizing.html
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-getoutputtimestamp.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-getoutputtimestamp.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html
index 9375cbd..cd09696e 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-getoutputtimestamp.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html
@@ -4,9 +4,9 @@
     <title>
       Testing AudioContext.getOutputTimestamp() method
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-suspend-resume.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html
similarity index 95%
rename from third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-suspend-resume.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html
index ee7d64a..48506d65 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontext-suspend-resume.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontext-suspend-resume.html
@@ -4,10 +4,10 @@
     <title>
       Test AudioContext.suspend() and AudioContext.resume()
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontextoptions.html b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
similarity index 96%
rename from third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontextoptions.html
rename to third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
index e77ed92..295cd5a 100644
--- a/third_party/WebKit/LayoutTests/webaudio/AudioContext/audiocontextoptions.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
@@ -4,9 +4,9 @@
     <title>
       Test AudioContextOptions
     </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit.js"></script>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
   </head>
   <body>
     <script id="layout-test-code">
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/sync-no-progress.any.js b/third_party/WebKit/LayoutTests/external/wpt/xhr/sync-no-progress.any.js
new file mode 100644
index 0000000..a915e4d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/sync-no-progress.any.js
@@ -0,0 +1,12 @@
+test(t => {
+  let xhr = new XMLHttpRequest();
+  let loadEventFired = false;
+  xhr.onprogress = t.unreached_func('progress event should not be fired');
+  xhr.onload = () => {
+    loadEventFired = true;
+  };
+  xhr.open('GET', 'resources/trickle.py?count=4&delay=150', false);
+  xhr.send();
+  // Check the load event as a sanity check that the test is working.
+  assert_true(loadEventFired, 'load event should have fired');
+}, 'progress event should not be fired by sync XHR');
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters-expected.txt b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters-expected.txt
index bb0dd50..720ce61 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
 PASS setParameters() changes getParameters() returned values
 FAIL setParameters() with large maxBitrate changes getParameters() returned values assert_equals: expected 4294967295 but got 2147483647
-PASS setParameters() with multiple calls
+PASS setParameters() with multiple calls after single getParameters()
 PASS setParameters() fails without getParameters()
 PASS video setParameters() check for transactionId modification
 FAIL video setParameters() check for transactionId removed assert_unreached: Should have rejected: undefined Reached unreachable code
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters.html
index 3df999b..9d7ff118 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters.html
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCRtpSender-setParameters.html
@@ -43,7 +43,7 @@
   assert_equals(videoParameters.encodings[0].maxBitrate, 0xFFFFFFFF);
 }, 'setParameters() with large maxBitrate changes getParameters() returned values');
 
-promise_test(async () => {
+promise_test(async (t) => {
   let pc1 = new RTCPeerConnection();
   let pc2 = new RTCPeerConnection();
 
@@ -60,14 +60,13 @@
   videoParameters.encodings[0].active = true;
   videoParameters.encodings[0].priority = 'low';
   videoParameters.encodings[0].maxBitrate = 1337001;
-  await videoSender.setParameters(videoParameters);
-
-  videoParameters = videoSender.getParameters();
-  assert_equals(videoParameters.encodings[0].active, true);
-  assert_equals(videoParameters.encodings[0].priority, 'low');
-  assert_equals(videoParameters.encodings[0].maxBitrate, 1337001);
-
-}, 'setParameters() with multiple calls');
+  return promise_rejects(
+    t,
+    new DOMException(
+      "getParameters() needs to be called before setParameters().",
+      "InvalidStateError"),
+    videoSender.setParameters(videoParameters));
+}, 'setParameters() with multiple calls after single getParameters()');
 
 promise_test(async (t) => {
   let pc1 = new RTCPeerConnection();
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-001-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-001-expected.txt
new file mode 100644
index 0000000..f5d8c0e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-001-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS spaces linebreak
+PASS linebreak spaces
+PASS spaces linebreak spaces
+PASS multiple linebreaks
+PASS multiple linebreaks + spaces
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-002-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-002-expected.txt
new file mode 100644
index 0000000..f5d8c0e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-002-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS spaces linebreak
+PASS linebreak spaces
+PASS spaces linebreak spaces
+PASS multiple linebreaks
+PASS multiple linebreaks + spaces
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-003-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-003-expected.txt
new file mode 100644
index 0000000..f5d8c0e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-003-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS spaces linebreak
+PASS linebreak spaces
+PASS spaces linebreak spaces
+PASS multiple linebreaks
+PASS multiple linebreaks + spaces
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-004-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-004-expected.txt
new file mode 100644
index 0000000..59f2b32f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-004-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS linebreak only â‚©ï¼’ï¼”
+PASS spaces linebreak â‚©ï¼’ï¼”
+PASS linebreak spaces â‚©ï¼’ï¼”
+PASS spaces linebreak spaces â‚©ï¼’ï¼”
+PASS multiple linebreaks â‚©ï¼’ï¼”
+PASS multiple linebreaks + spaces â‚©ï¼’ï¼”
+PASS linebreak only 24₩
+PASS spaces linebreak 24₩
+PASS linebreak spaces 24₩
+PASS spaces linebreak spaces 24₩
+PASS multiple linebreaks 24₩
+PASS multiple linebreaks + spaces 24₩
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-008-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-008-expected.txt
new file mode 100644
index 0000000..f5d8c0e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-008-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS spaces linebreak
+PASS linebreak spaces
+PASS spaces linebreak spaces
+PASS multiple linebreaks
+PASS multiple linebreaks + spaces
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-009-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-009-expected.txt
new file mode 100644
index 0000000..f5d8c0e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-009-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS spaces linebreak
+PASS linebreak spaces
+PASS spaces linebreak spaces
+PASS multiple linebreaks
+PASS multiple linebreaks + spaces
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-016-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-016-expected.txt
new file mode 100644
index 0000000..0a4bad7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-016-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS zwsp retained 1
+PASS spaces linebreak
+PASS zwsp retained 2
+PASS linebreak spaces
+PASS zwsp retained 3
+PASS spaces linebreak spaces
+PASS zwsp retained 4
+PASS multiple linebreaks
+PASS zwsp retained 5
+PASS multiple linebreaks + spaces
+PASS zwsp retained 6
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-017-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-017-expected.txt
new file mode 100644
index 0000000..0a4bad7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-text/white-space/seg-break-transformation-017-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS linebreak only
+PASS zwsp retained 1
+PASS spaces linebreak
+PASS zwsp retained 2
+PASS linebreak spaces
+PASS zwsp retained 3
+PASS spaces linebreak spaces
+PASS zwsp retained 4
+PASS multiple linebreaks
+PASS zwsp retained 5
+PASS multiple linebreaks + spaces
+PASS zwsp retained 6
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-002-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-002-expected.txt
new file mode 100644
index 0000000..23dd3f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-002-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS Checking whether dynamic changes to visibility interact correctly with
+  table anonymous boxes
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-003-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-003-expected.txt
new file mode 100644
index 0000000..23dd3f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/cssom-view/elementFromPoint-003-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS Checking whether dynamic changes to visibility interact correctly with
+  table anonymous boxes
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt
new file mode 100644
index 0000000..d765b87
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt
@@ -0,0 +1,217 @@
+This is a testharness.js-based test.
+Found 213 tests; 138 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Simplest possible test ("<div>abc")
+PASS Leading whitespace removed ("<div> abc")
+PASS Trailing whitespace removed ("<div>abc ")
+PASS Internal whitespace compressed ("<div>abc  def")
+PASS \n converted to space ("<div>abc\ndef")
+PASS \r converted to space ("<div>abc\rdef")
+PASS \t converted to space ("<div>abc\tdef")
+PASS Trailing whitespace before hard line break removed ("<div>abc <br>def")
+PASS Leading whitespace preserved ("<pre> abc")
+PASS Trailing whitespace preserved ("<pre>abc ")
+PASS Internal whitespace preserved ("<pre>abc  def")
+PASS \n preserved ("<pre>abc\ndef")
+PASS \r converted to newline ("<pre>abc\rdef")
+PASS \t preserved ("<pre>abc\tdef")
+FAIL Two <pre> siblings ("<div><pre>abc</pre><pre>def</pre>") assert_equals: expected "abc\ndef" but got "abc\ndef\n"
+PASS Leading whitespace preserved ("<div style='white-space:pre'> abc")
+PASS Trailing whitespace preserved ("<div style='white-space:pre'>abc ")
+PASS Internal whitespace preserved ("<div style='white-space:pre'>abc  def")
+PASS \n preserved ("<div style='white-space:pre'>abc\ndef")
+PASS \r converted to newline ("<div style='white-space:pre'>abc\rdef")
+PASS \t preserved ("<div style='white-space:pre'>abc\tdef")
+PASS Leading whitespace preserved ("<span style='white-space:pre'> abc")
+PASS Trailing whitespace preserved ("<span style='white-space:pre'>abc ")
+PASS Internal whitespace preserved ("<span style='white-space:pre'>abc  def")
+PASS \n preserved ("<span style='white-space:pre'>abc\ndef")
+PASS \r converted to newline ("<span style='white-space:pre'>abc\rdef")
+PASS \t preserved ("<span style='white-space:pre'>abc\tdef")
+PASS Leading whitespace removed ("<div style='white-space:pre-line'> abc")
+PASS Trailing whitespace removed ("<div style='white-space:pre-line'>abc ")
+PASS Internal whitespace collapsed ("<div style='white-space:pre-line'>abc  def")
+PASS \n preserved ("<div style='white-space:pre-line'>abc\ndef")
+PASS \r converted to newline ("<div style='white-space:pre-line'>abc\rdef")
+PASS \t converted to space ("<div style='white-space:pre-line'>abc\tdef")
+PASS Whitespace collapses across element boundaries ("<div><span>abc </span> def")
+PASS Whitespace collapses across element boundaries ("<div><span>abc </span><span></span> def")
+PASS Whitespace collapses across element boundaries ("<div><span>abc </span><span style='white-space:pre'></span> def")
+PASS Soft line breaks ignored ("<div style='width:0'>abc def")
+FAIL ::first-line styles applied ("<div class='first-line-uppercase' style='width:0'>abc def") assert_equals: expected "ABC def" but got "abc def"
+PASS ::first-letter styles applied ("<div class='first-letter-uppercase' style='width:0'>abc def")
+PASS ::first-letter float ignored ("<div class='first-letter-float' style='width:0'>abc def")
+PASS &nbsp; preserved ("<div>&nbsp;")
+PASS display:none container ("<div style='display:none'>abc")
+PASS No whitespace compression in display:none container ("<div style='display:none'>abc  def")
+PASS No removal of leading/trailing whitespace in display:none container ("<div style='display:none'> abc def ")
+PASS display:none child not rendered ("<div>123<span style='display:none'>abc")
+PASS display:none container with non-display-none target child ("<div style='display:none'><span id='target'>abc")
+FAIL non-display-none child of svg ("<div id='target'>abc") assert_equals: expected "" but got "abc"
+PASS display:none child of svg ("<div style='display:none' id='target'>abc")
+PASS child of display:none child of svg ("<div style='display:none'><div id='target'>abc")
+PASS display:contents container ("<div style='display:contents'>abc")
+PASS display:contents container ("<div><div style='display:contents'>abc")
+PASS display:contents rendered ("<div>123<span style='display:contents'>abc")
+FAIL display:contents not processed via textContent ("<div style='display:contents'>   ") assert_equals: expected "" but got "   "
+PASS display:contents not processed via textContent ("<div><div style='display:contents'>   ")
+PASS visibility:hidden container ("<div style='visibility:hidden'>abc")
+PASS visibility:hidden child not rendered ("<div>123<span style='visibility:hidden'>abc")
+PASS visibility:visible child rendered ("<div style='visibility:hidden'>123<span style='visibility:visible'>abc")
+PASS visibility:collapse row-group ("<table><tbody style='visibility:collapse'><tr><td>abc")
+PASS visibility:collapse row ("<table><tr style='visibility:collapse'><td>abc")
+PASS visibility:collapse cell ("<table><tr><td style='visibility:collapse'>abc")
+FAIL visibility:collapse row-group with visible cell ("<table><tbody style='visibility:collapse'><tr><td style='visibility:visible'>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL visibility:collapse row with visible cell ("<table><tr style='visibility:collapse'><td style='visibility:visible'>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL visibility:collapse honored on flex item ("<div style='display:flex'><span style='visibility:collapse'>1</span><span>2</span></div>") assert_equals: expected "2" but got "2\n"
+FAIL visibility:collapse honored on grid item ("<div style='display:grid'><span style='visibility:collapse'>1</span><span>2</span></div>") assert_equals: expected "2" but got "2\n"
+PASS opacity:0 container ("<div style='opacity:0'>abc")
+PASS Whitespace compression in opacity:0 container ("<div style='opacity:0'>abc  def")
+PASS Remove leading/trailing whitespace in opacity:0 container ("<div style='opacity:0'> abc def ")
+PASS opacity:0 child rendered ("<div>123<span style='opacity:0'>abc")
+PASS Generated content not included ("<div class='before'>")
+PASS Generated content on child not included ("<div><div class='before'>")
+PASS <button> contents preserved ("<button>abc")
+PASS <fieldset> contents preserved ("<fieldset>abc")
+FAIL <fieldset> <legend> contents preserved ("<fieldset><legend>abc") assert_equals: expected "abc" but got "abc\n"
+PASS <input> contents ignored ("<input type='text' value='abc'>")
+PASS <textarea> contents ignored ("<textarea>abc")
+PASS <iframe> contents ignored ("<iframe>abc")
+PASS <iframe> contents ignored ("<iframe><div id='target'>abc")
+PASS <iframe> subdocument ignored ("<iframe src='data:text/html,abc'>")
+FAIL <audio> contents ignored ("<audio style='display:block'>abc") assert_equals: expected "" but got "abc"
+FAIL <audio> contents ignored ("<audio style='display:block'><source id='target' class='poke' style='display:block'>") assert_equals: expected "" but got "abc"
+PASS <audio> contents ok if display:none ("<audio style='display:block'><source id='target' class='poke' style='display:none'>")
+PASS <video> contents ignored ("<video>abc")
+FAIL <video> contents ignored ("<video style='display:block'><source id='target' class='poke' style='display:block'>") assert_equals: expected "" but got "abc"
+PASS <video> contents ok if display:none ("<video style='display:block'><source id='target' class='poke' style='display:none'>")
+PASS <canvas> contents ignored ("<canvas>abc")
+FAIL <canvas><div id='target'> contents ignored ("<canvas><div id='target'>abc") assert_equals: expected "" but got "abc"
+PASS <img> alt text ignored ("<img alt='abc'>")
+PASS <img> contents ignored ("<img src='about:blank' class='poke'>")
+FAIL <select size='1'> contents of options preserved ("<select size='1'><option>abc</option><option>def") assert_equals: expected "abc\ndef" but got ""
+FAIL <select size='2'> contents of options preserved ("<select size='2'><option>abc</option><option>def") assert_equals: expected "abc\ndef" but got ""
+PASS <select size='1'> contents of target option preserved ("<select size='1'><option id='target'>abc</option><option>def")
+PASS <select size='2'> contents of target option preserved ("<select size='2'><option id='target'>abc</option><option>def")
+PASS empty <select> ("<div>a<select></select>bc")
+FAIL empty <optgroup> in <select> ("<div>a<select><optgroup></select>bc") assert_equals: expected "a\nbc" but got "abc"
+FAIL empty <option> in <select> ("<div>a<select><option></select>bc") assert_equals: expected "a\nbc" but got "abc"
+PASS <select> containing text node child ("<select class='poke'></select>")
+PASS <optgroup> containing <optgroup> ("<select><optgroup class='poke-optgroup'></select>")
+FAIL <optgroup> containing <option> ("<select><optgroup><option>abc</select>") assert_equals: expected "abc" but got ""
+FAIL <div> in <option> ("<select><option class='poke-div'>123</select>") assert_equals: expected "123\nabc" but got ""
+FAIL empty <optgroup> in <div> ("<div>a<optgroup></optgroup>bc") assert_equals: expected "a\nbc" but got "abc"
+FAIL <optgroup> in <div> ("<div>a<optgroup>123</optgroup>bc") assert_equals: expected "a\nbc" but got "abc"
+FAIL empty <option> in <div> ("<div>a<option></option>bc") assert_equals: expected "a\nbc" but got "abc"
+FAIL <option> in <div> ("<div>a<option>123</option>bc") assert_equals: expected "a\n123\nbc" but got "abc"
+PASS <button> contents preserved ("<div><button>abc")
+FAIL <fieldset> contents preserved ("<div><fieldset>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL <fieldset> <legend> contents preserved ("<div><fieldset><legend>abc") assert_equals: expected "abc" but got "abc\n"
+PASS <input> contents ignored ("<div><input type='text' value='abc'>")
+PASS <textarea> contents ignored ("<div><textarea>abc")
+FAIL <select size='1'> contents of options preserved ("<div><select size='1'><option>abc</option><option>def") assert_equals: expected "abc\ndef" but got ""
+FAIL <select size='2'> contents of options preserved ("<div><select size='2'><option>abc</option><option>def") assert_equals: expected "abc\ndef" but got ""
+PASS <iframe> contents ignored ("<div><iframe>abc")
+PASS  <iframe> subdocument ignored ("<div><iframe src='data:text/html,abc'>")
+PASS <audio> contents ignored ("<div><audio>abc")
+PASS <video> contents ignored ("<div><video>abc")
+PASS <canvas> contents ignored ("<div><canvas>abc")
+PASS <img> alt text ignored ("<div><img alt='abc'>")
+PASS Newline at block boundary ("<div>123<div>abc</div>def")
+PASS Newline at display:block boundary ("<div>123<span style='display:block'>abc</span>def")
+PASS Empty block induces single line break ("<div>abc<div></div>def")
+PASS Consecutive empty blocks ignored ("<div>abc<div></div><div></div>def")
+FAIL No blank lines around <p> alone ("<div><p>abc") assert_equals: expected "abc" but got "abc\n\n"
+FAIL No blank lines around <p> followed by only collapsible whitespace ("<div><p>abc</p> ") assert_equals: expected "abc" but got "abc\n\n"
+FAIL No blank lines around <p> preceded by only collapsible whitespace ("<div> <p>abc</p>") assert_equals: expected "abc" but got "abc\n\n"
+FAIL Blank line between consecutive <p>s ("<div><p>abc<p>def") assert_equals: expected "abc\n\ndef" but got "abc\n\ndef\n\n"
+FAIL Blank line between consecutive <p>s separated only by collapsible whitespace ("<div><p>abc</p> <p>def") assert_equals: expected "abc\n\ndef" but got "abc\n\ndef\n\n"
+FAIL Blank line between consecutive <p>s separated only by empty block ("<div><p>abc</p><div></div><p>def") assert_equals: expected "abc\n\ndef" but got "abc\n\ndef\n\n"
+FAIL Blank lines between <p>s separated by non-empty block ("<div><p>abc</p><div>123</div><p>def") assert_equals: expected "abc\n\n123\n\ndef" but got "abc\n\n123\ndef\n\n"
+FAIL Blank lines around a <p> in its own block ("<div>abc<div><p>123</p></div>def") assert_equals: expected "abc\n\n123\n\ndef" but got "abc\n123\n\ndef"
+FAIL Blank line before <p> ("<div>abc<p>def") assert_equals: expected "abc\n\ndef" but got "abc\ndef\n\n"
+PASS Blank line after <p> ("<div><p>abc</p>def")
+FAIL One blank line between <p>s, ignoring empty <p>s ("<div><p>abc<p></p><p></p><p>def") assert_equals: expected "abc\n\ndef" but got "abc\n\ndef\n\n"
+FAIL Invisible <p> doesn't induce extra line breaks ("<div style='visibility:hidden'><p><span style='visibility:visible'>abc</span></p>\n<div style='visibility:visible'>def</div>") assert_equals: expected "abc\ndef" but got "abc\n\ndef\n"
+FAIL No blank lines around <div> with margin ("<div>abc<div style='margin:2em'>def") assert_equals: expected "abc\ndef" but got "abc\ndef\n"
+PASS No newlines at display:inline-block boundary ("<div>123<span style='display:inline-block'>abc</span>def")
+PASS Leading/trailing space removal at display:inline-block boundary ("<div>123<span style='display:inline-block'> abc </span>def")
+FAIL Blank lines around <p> even without margin ("<div>123<p style='margin:0px'>abc</p>def") assert_equals: expected "123\n\nabc\n\ndef" but got "123\nabc\n\ndef"
+PASS No blank lines around <h1> ("<div>123<h1>abc</h1>def")
+PASS No blank lines around <h2> ("<div>123<h2>abc</h2>def")
+PASS No blank lines around <h3> ("<div>123<h3>abc</h3>def")
+PASS No blank lines around <h4> ("<div>123<h4>abc</h4>def")
+PASS No blank lines around <h5> ("<div>123<h5>abc</h5>def")
+PASS No blank lines around <h6> ("<div>123<h6>abc</h6>def")
+PASS <span> boundaries are irrelevant ("<div>123<span>abc</span>def")
+PASS <span> boundaries are irrelevant ("<div>123 <span>abc</span> def")
+PASS <span> boundaries are irrelevant ("<div style='width:0'>123 <span>abc</span> def")
+PASS <em> gets no special treatment ("<div>123<em>abc</em>def")
+PASS <b> gets no special treatment ("<div>123<b>abc</b>def")
+PASS <i> gets no special treatment ("<div>123<i>abc</i>def")
+PASS <strong> gets no special treatment ("<div>123<strong>abc</strong>def")
+PASS <tt> gets no special treatment ("<div>123<tt>abc</tt>def")
+PASS <code> gets no special treatment ("<div>123<code>abc</code>def")
+PASS soft hyphen preserved ("<div>abc&shy;def")
+PASS soft hyphen preserved ("<div style='width:0'>abc&shy;def")
+FAIL Ignoring non-rendered table whitespace ("<div><table style='white-space:pre'>  <td>abc</td>  </table>") assert_equals: expected "abc" but got "abc\n"
+FAIL Tab-separated table cells ("<div><table><tr><td>abc<td>def</table>") assert_equals: expected "abc\tdef" but got "abc\tdef\n"
+FAIL Tab-separated table cells including empty cells ("<div><table><tr><td>abc<td><td>def</table>") assert_equals: expected "abc\t\tdef" but got "abc\t\tdef\n"
+FAIL Tab-separated table cells including trailing empty cells ("<div><table><tr><td>abc<td><td></table>") assert_equals: expected "abc\t\t" but got "abc\t\t\n"
+FAIL Newline-separated table rows ("<div><table><tr><td>abc<tr><td>def</table>") assert_equals: expected "abc\ndef" but got "abc\ndef\n"
+PASS Newlines around table ("<div>abc<table><td>def</table>ghi")
+FAIL Tab-separated table cells in a border-collapse table ("<div><table style='border-collapse:collapse'><tr><td>abc<td>def</table>") assert_equals: expected "abc\tdef" but got "abc\tdef\n"
+FAIL tfoot not reordered ("<div><table><tfoot>x</tfoot><tbody>y</tbody></table>") assert_equals: expected "xy" but got "xy\n"
+FAIL  ("<table><tfoot><tr><td>footer</tfoot><thead><tr><td style='visibility:collapse'>thead</thead><tbody><tr><td>tbody</tbody></table>") assert_equals: expected "footer\n\ntbody" but got "footer\ntbody\n"
+FAIL Newline between cells and caption ("<div><table><tr><td>abc<caption>def</caption></table>") assert_equals: expected "abc\ndef" but got "abc\ndef\n"
+FAIL Tab-separated table cells ("<div><div class='table'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>") assert_equals: expected "abc\tdef" but got "abc\tdef\n"
+FAIL Newline-separated table rows ("<div><div class='table'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>") assert_equals: expected "abc\ndef" but got "abc\ndef\n"
+PASS Newlines around table ("<div>abc<div class='table'><span class='cell'>def</span></div>ghi")
+FAIL Tab-separated table cells ("<div><div class='itable'><span class='cell'>abc</span>\n<span class='cell'>def</span></div>") assert_equals: expected "abc\tdef" but got "abc\tdef "
+FAIL Newline-separated table rows ("<div><div class='itable'><span class='row'><span class='cell'>abc</span></span>\n<span class='row'><span class='cell'>def</span></span></div>") assert_equals: expected "abc\ndef" but got "abc\tdef "
+FAIL No newlines around inline-table ("<div>abc<div class='itable'><span class='cell'>def</span></div>ghi") assert_equals: expected "abcdefghi" but got "abc def ghi"
+FAIL Single newline in two-row inline-table ("<div>abc<div class='itable'><span class='row'><span class='cell'>def</span></span>\n<span class='row'><span class='cell'>123</span></span></div>ghi") assert_equals: expected "abcdef\n123ghi" but got "abc def\t123 ghi"
+FAIL <ol> list items get no special treatment ("<div><ol><li>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL <ul> list items get no special treatment ("<div><ul><li>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL display:block <script> is rendered ("<div><script style='display:block'>abc") assert_equals: expected "abc" but got "abc\n"
+FAIL display:block <style> is rendered ("<div><style style='display:block'>abc") assert_equals: expected "abc" but got "abc\n"
+PASS display:block <noscript> is not rendered (it's not parsed!) ("<div><noscript style='display:block'>abc")
+PASS display:block <template> contents are not rendered (the contents are in a different document) ("<div><template style='display:block'>abc")
+PASS <br> induces line break ("<div>abc<br>def")
+PASS <br> induces line break even at end of block ("<div>abc<br>")
+PASS <br> content ignored ("<div><br class='poke'>")
+PASS <hr> induces line break ("<div>abc<hr>def")
+PASS <hr><hr> induces just one line break ("<div>abc<hr><hr>def")
+PASS <hr><hr><hr> induces just one line break ("<div>abc<hr><hr><hr>def")
+FAIL <hr> content rendered ("<div><hr class='poke'>") assert_equals: expected "abc" but got "abc\n"
+PASS comment ignored ("<div>abc<!--comment-->def")
+FAIL text-transform is applied ("<div><div style='text-transform:uppercase'>abc") assert_equals: expected "ABC" but got "ABC\n"
+FAIL text-transform handles es-zet ("<div><div style='text-transform:uppercase'>Maß") assert_equals: expected "MASS" but got "MAS\n"
+FAIL text-transform handles Turkish casing ("<div><div lang='tr' style='text-transform:uppercase'>i ı") assert_equals: expected "İ I" but got "İ I\n"
+PASS block-in-inline doesn't add unnecessary newlines ("<div>abc<span>123<div>456</div>789</span>def")
+FAIL floats induce a block boundary ("<div>abc<div style='float:left'>123</div>def") assert_equals: expected "abc\n123\ndef" but got "abc123def"
+FAIL floats induce a block boundary ("<div>abc<span style='float:left'>123</span>def") assert_equals: expected "abc\n123\ndef" but got "abc123def"
+FAIL position:absolute induces a block boundary ("<div>abc<div style='position:absolute'>123</div>def") assert_equals: expected "abc\n123\ndef" but got "abc123def"
+FAIL position:absolute induces a block boundary ("<div>abc<span style='position:absolute'>123</span>def") assert_equals: expected "abc\n123\ndef" but got "abc123def"
+PASS position:relative has no effect ("<div>abc<div style='position:relative'>123</div>def")
+PASS position:relative has no effect ("<div>abc<span style='position:relative'>123</span>def")
+PASS overflow:hidden ignored ("<div style='overflow:hidden'>abc")
+FAIL overflow:hidden ignored even with zero width ("<div style='width:0; overflow:hidden'>abc") assert_equals: expected "abc" but got ""
+FAIL overflow:hidden ignored even with zero height ("<div style='height:0; overflow:hidden'>abc") assert_equals: expected "abc" but got ""
+FAIL text-overflow:ellipsis ignored ("<div style='width:0; overflow:hidden; text-overflow:ellipsis'>abc") assert_equals: expected "abc" but got ""
+PASS innerText not supported on SVG elements ("<svg>abc")
+PASS innerText not supported on MathML elements ("<math>abc")
+PASS <rt> and no <rp> ("<div><ruby>abc<rt>def</rt></ruby>")
+PASS <rp> ("<div><ruby>abc<rp>(</rp><rt>def</rt><rp>)</rp></ruby>")
+FAIL Lone <rp> ("<div><rp>abc</rp>") assert_equals: expected "" but got "abc"
+PASS visibility:hidden <rp> ("<div><rp style='visibility:hidden'>abc</rp>")
+PASS display:block <rp> ("<div><rp style='display:block'>abc</rp>def")
+PASS display:block <rp> with whitespace ("<div><rp style='display:block'> abc </rp>def")
+PASS <rp> in a <select> ("<div><select class='poke-rp'></select>")
+PASS Shadow DOM contents ignored ("<div class='shadow'>")
+PASS Shadow DOM contents ignored ("<div><div class='shadow'>")
+FAIL CSS 'order' property ignored ("<div style='display:flex'><div style='order:1'>1</div><div>2</div></div>") assert_equals: expected "1\n2" but got "1\n2\n"
+FAIL Flex items blockified ("<div style='display:flex'><span>1</span><span>2</span></div>") assert_equals: expected "1\n2" but got "1\n2\n"
+FAIL CSS 'order' property ignored ("<div style='display:grid'><div style='order:1'>1</div><div>2</div></div>") assert_equals: expected "1\n2" but got "1\n2\n"
+FAIL Grid items blockified ("<div style='display:grid'><span>1</span><span>2</span></div>") assert_equals: expected "1\n2" but got "1\n2\n"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-expected.txt
new file mode 100644
index 0000000..b5a628c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/scrollbar-layer-placement-expected.txt
@@ -0,0 +1,188 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='outer A'",
+      "position": [6, 6],
+      "bounds": [340, 282]
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='scroller'",
+      "position": [38, 38],
+      "bounds": [278, 218],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [67, 67],
+      "bounds": [220, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [67, 67],
+      "bounds": [220, 236],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [62, 62],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [280, 67],
+      "bounds": [7, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='outer B'",
+      "position": [355, 6],
+      "bounds": [340, 282]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='scroller'",
+      "position": [387, 38],
+      "bounds": [278, 218],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [416, 67],
+      "bounds": [220, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [416, 67],
+      "bounds": [220, 236],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='content'",
+      "position": [428, 79],
+      "bounds": [196, 212],
+      "contentsOpaque": true,
+      "backgroundColor": "#DDDDDD"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [411, 62],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [629, 67],
+      "bounds": [7, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='outer C'",
+      "position": [6, 297],
+      "bounds": [340, 282]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='scroller'",
+      "position": [37, 328],
+      "bounds": [278, 218],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [66, 357],
+      "bounds": [220, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [66, 357],
+      "bounds": [220, 236],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='content'",
+      "position": [78, 369],
+      "bounds": [196, 212],
+      "contentsOpaque": true,
+      "backgroundColor": "#DDDDDD"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [61, 352],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [279, 357],
+      "bounds": [7, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='outer D'",
+      "position": [355, 297],
+      "bounds": [340, 282]
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='clipper'",
+      "position": [379, 321],
+      "bounds": [292, 200]
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='scroller'",
+      "position": [386, 328],
+      "bounds": [278, 218],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [415, 357],
+      "bounds": [220, 160],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [415, 357],
+      "bounds": [220, 236],
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='content'",
+      "position": [427, 369],
+      "bounds": [196, 212],
+      "contentsOpaque": true,
+      "backgroundColor": "#DDDDDD"
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [410, 352],
+      "bounds": [230, 170],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [628, 357],
+      "bounds": [7, 160],
+      "drawsContent": false
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment-expected.txt
index 8e34c07..556d4d4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment-expected.txt
@@ -1,7 +1,7 @@
 Tests how fragment works.
 
 f1.outerHTML:
-<div-a attr="val" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
+<div-a attr="val" class=""><div-b foo1="bar1" s-state1-attr="val-state1" class="" foo2="bar2"></div-b><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
       <div></div></div-a>
 () => diva === f1.element()
   true
@@ -11,13 +11,9 @@
   true
 () => divc.tagName === 'DIV-C'
   true
-() => shadow.nodeType === Node.DOCUMENT_FRAGMENT_NODE
-  true
-() => shadow.parentElementOrShadowHost() === diva
-  true
 () => divc.parentNode === diva
   true
-() => divb.parentElementOrShadowHost() === diva
+() => divb.parentNode === diva
   true
 () => diva.lastChild === inner
   true
@@ -39,31 +35,15 @@
   true
 () => divb.getAttribute('attr') === null
   true
-() => diva.getAttribute('attr') === 'val-state1'
-  true
-() => divb.getAttribute('attr') === 'val-state1'
-  true
-() => diva.getAttribute('attr') === 'val-state1'
-  true
-() => divb.getAttribute('attr') === 'val-state1'
-  true
-() => diva.getAttribute('attr') === 'val'
-  true
-() => divb.getAttribute('attr') === null
-  true
-() => diva.getAttribute('attr') === 'val-state2'
-  true
-() => divb.getAttribute('attr') === null
-  true
 
 f2.outerHTML:
-<div><div-a attr="val-state2" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
+<div><div-a attr="val" class=""><div-b foo1="bar1" s-state1-attr="val-state1" class="" foo2="bar2"></div-b><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
       <div></div></div-a></div>
 () => f2.element().firstChild === f1.element()
   true
 
 f3.outerHTML:
-<div><div><div-a attr="val-state2" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
+<div><div><div-a attr="val" class=""><div-b foo1="bar1" s-state1-attr="val-state1" class="" foo2="bar2"></div-b><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
       <div></div></div-a></div></div>
 () => f3.element().firstChild === f2.element()
   true
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment.js b/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment.js
index 5927e60..80b424c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/components/fragment.js
@@ -11,11 +11,9 @@
 
   var inner = document.createElement('div');
   var f1 = UI.Fragment.build`
-    <div-a $=name-a attr=val s-state1-attr=val-state1 s-state2-attr=val-state2>
-      <x-shadow $=name-shadow>
-        <div-b $=name-b foo1=bar1 foo${'2'}=${'b'}ar${'2'} ${''} ${element => element.divb = true} s-state1-attr=val-state1>
-        </div-b>
-      </x-shadow>
+    <div-a $=name-a attr=val>
+      <div-b $=name-b foo1=bar1 foo${'2'}=${'b'}ar${'2'} ${''} ${element => element.divb = true} s-state1-attr=val-state1>
+      </div-b>
       <div-c $=name-c class='${'my-class-1'} my-class-2' ${'foo'}=bar>${'Some text here'} ${'And more text'}</div-c>
       ${inner}
     </div-a>
@@ -27,16 +25,13 @@
   var diva = f1.$('name-a');
   var divb = f1.$('name-b');
   var divc = f1.$('name-c');
-  var shadow = f1.$('name-shadow');
 
   check(() => diva === f1.element());
   check(() => diva.tagName === 'DIV-A');
   check(() => divb.tagName === 'DIV-B');
   check(() => divc.tagName === 'DIV-C');
-  check(() => shadow.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
-  check(() => shadow.parentElementOrShadowHost() === diva);
   check(() => divc.parentNode === diva);
-  check(() => divb.parentElementOrShadowHost() === diva);
+  check(() => divb.parentNode === diva);
   check(() => diva.lastChild === inner);
 
   check(() => divb.getAttribute('foo1') === 'bar1');
@@ -50,18 +45,6 @@
 
   check(() => diva.getAttribute('attr') === 'val');
   check(() => divb.getAttribute('attr') === null);
-  f1.setState('state1', true);
-  check(() => diva.getAttribute('attr') === 'val-state1');
-  check(() => divb.getAttribute('attr') === 'val-state1');
-  f1.setState('state1', true);
-  check(() => diva.getAttribute('attr') === 'val-state1');
-  check(() => divb.getAttribute('attr') === 'val-state1');
-  f1.setState('state1', false);
-  check(() => diva.getAttribute('attr') === 'val');
-  check(() => divb.getAttribute('attr') === null);
-  f1.setState('state2', true);
-  check(() => diva.getAttribute('attr') === 'val-state2');
-  check(() => divb.getAttribute('attr') === null);
 
   TestRunner.addResult('');
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search-expected.txt
index c20d677..72462d87 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search-expected.txt
@@ -1,5 +1,37 @@
 Tests search in network requests
 
+URL search
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=02
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=0
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=12
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=1
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=22
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=2
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=32
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=3
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=42
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=4
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=52
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=5
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=62
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=6
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=72
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=7
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=82
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=8
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+  echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=92
+  URLhttp://127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=9
+  Referer:http://127.0.0.1:8000/devtools/resources/inspected-page.html
+
 Ignore case, regexp
   echo-payload.php—127.0.0.1:8000/devtools/network/resources/echo-payload.php?n=01
   1request0-dosearch-doSearch-d.Search
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search.js b/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search.js
index 071d628..6ddf862 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/network/network-search.js
@@ -27,9 +27,9 @@
     return treeElement;
   }
 
-  async function search(label, isRegex, ignoreCase) {
+  async function search(label, isRegex, ignoreCase, query = 'd.search') {
     TestRunner.addResult(label);
-    const view = await Network.SearchNetworkView.openSearch('d.search');
+    const view = await Network.SearchNetworkView.openSearch(query);
     view._matchCaseButton.setToggled(!ignoreCase);
     view._regexButton.setToggled(isRegex);
     const promise = TestRunner.addSnifferPromise(view, '_searchFinished');
@@ -72,6 +72,7 @@
       ConsoleTestRunner.addConsoleSniffer(step2);
       return;
     }
+    await search('URL search', true, true, '8000/devtools');
     await search('Ignore case, regexp', true, true);
     await search('Ignore case, No regexp', false, true);
     const lastResult = await search('Case sensitive, regexp', true, false);
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.txt b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.txt
index e277e977..1bae546 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.txt
@@ -11,7 +11,6 @@
     layer at (0,0) size 440x168
       LayoutBlockFlow {HTML} at (0,0) size 440x168
         LayoutBlockFlow {BODY} at (8,8) size 424x152
+          LayoutImage {IMG} at (0,0) size 200x150
           LayoutImage {IMG} at (200,0) size 200x150
           LayoutText {#text} at (0,0) size 0x0
-    layer at (8,8) size 200x150
-      LayoutImage {IMG} at (0,0) size 200x150
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.txt b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.txt
index 9838c8d..1bae546 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.txt
@@ -12,6 +12,5 @@
       LayoutBlockFlow {HTML} at (0,0) size 440x168
         LayoutBlockFlow {BODY} at (8,8) size 424x152
           LayoutImage {IMG} at (0,0) size 200x150
+          LayoutImage {IMG} at (200,0) size 200x150
           LayoutText {#text} at (0,0) size 0x0
-    layer at (208,8) size 200x150
-      LayoutImage {IMG} at (200,0) size 200x150
diff --git a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/url/url-setters-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/external/wpt/url/url-setters-expected.txt
index 905255f..fd516ea 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/url/url-setters-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 589 tests; 293 PASS, 296 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 592 tests; 296 PASS, 296 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
 PASS <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
@@ -244,6 +244,9 @@
 PASS URL: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <a>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <area>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
+PASS URL: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <a>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <area>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
 FAIL URL: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <a>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <area>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/url/url-setters-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/url/url-setters-expected.txt
index 905255f..fd516ea 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/url/url-setters-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 589 tests; 293 PASS, 296 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 592 tests; 296 PASS, 296 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
 PASS <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
@@ -244,6 +244,9 @@
 PASS URL: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <a>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <area>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
+PASS URL: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <a>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <area>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
 FAIL URL: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <a>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <area>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
diff --git a/third_party/WebKit/LayoutTests/platform/win/external/wpt/url/url-setters-expected.txt b/third_party/WebKit/LayoutTests/platform/win/external/wpt/url/url-setters-expected.txt
index 3975ed3..7b91a90 100644
--- a/third_party/WebKit/LayoutTests/platform/win/external/wpt/url/url-setters-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 589 tests; 266 PASS, 323 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 592 tests; 269 PASS, 323 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 FAIL URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged. assert_equals: expected "a://example.net" but got "file:///A://example.net"
 FAIL <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged. assert_equals: expected "a://example.net" but got "file:///A://example.net"
@@ -244,6 +244,9 @@
 PASS URL: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <a>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
 PASS <area>: Setting <https://example.net>.host = 'example.com:80' Default port number is only removed for the relevant scheme
+PASS URL: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <a>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
+PASS <area>: Setting <http://example.net:8080>.host = 'example.com:80' Port number is removed if new port is scheme default and existing URL has a non-default port
 FAIL URL: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <a>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
 FAIL <area>: Setting <http://example.net/path>.host = 'example.com/stuff' Stuff after a / delimiter is ignored assert_equals: expected "http://example.com/path" but got "http://example.com%2Fstuff/path"
diff --git a/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad-expected.txt b/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad-expected.txt
new file mode 100644
index 0000000..b17e8310
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad-expected.txt
@@ -0,0 +1,4 @@
+CONSOLE MESSAGE: line 33: This test requires User Activation V2.
+EXPECTED (internals.runtimeFlags.userActivationV2Enabled == 'false') OK
+END OF TEST
+
diff --git a/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad.html b/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad.html
new file mode 100644
index 0000000..a98e4891
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/user-activation-v2/full-screen-gamepad.html
@@ -0,0 +1,49 @@
+<body>
+<script src="../fullscreen/full-screen-test.js"></script>
+<span></span>
+<script>
+    var span = document.getElementsByTagName('span')[0];
+
+    var documentEnteredFullscreen = function() {
+        testExpected("document.fullscreenElement", document.documentElement);
+        span.requestFullscreen();
+        waitForEvent(document, 'fullscreenchange', spanEnteredFullscreen, false, true);
+    };
+
+    var spanEnteredFullscreen = function() {
+        testExpected("document.fullscreenElement", span);
+        waitForEvent(document, 'fullscreenchange', spanExited, false, true);
+        document.exitFullscreen();
+    };
+
+    var spanExited = function() {
+        testExpected("document.fullscreenElement", document.documentElement);
+        waitForEvent(document, 'fullscreenchange', documentExited, false, true);
+        document.exitFullscreen();
+    };
+
+    var documentExited = function() {
+        testExpected("document.fullscreenElement", undefined);
+        endTest();
+    };
+
+    testExpected("internals.runtimeFlags.userActivationV2Enabled",
+                 internals.runtimeFlags.userActivationV2Enabled);
+    if (!internals.runtimeFlags.userActivationV2Enabled) {
+        console.log("This test requires User Activation V2.");
+        endTest();
+    } else {
+        if (window.gamepadController) {
+            // Simulate pressing a button on a connected gamepad. A gamepad
+            // button press should be treated as a user activation for the
+            // fullscreen API.
+            gamepadController.connect(0);
+            gamepadController.setId(0, "FullScream Maximizer");
+            gamepadController.setButtonCount(0, 1);
+            gamepadController.setButtonData(0, 0, 1);
+            navigator.getGamepads();
+        }
+        document.documentElement.requestFullscreen();
+        waitForEvent(document, 'fullscreenchange', documentEnteredFullscreen, false, true);
+    }
+</script>
diff --git a/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/README.txt b/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/README.txt
new file mode 100644
index 0000000..4f3b7b6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/README.txt
@@ -0,0 +1 @@
+# This suite runs tests with --enable-features=UserActivationV2.
diff --git a/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/full-screen-gamepad-expected.txt b/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/full-screen-gamepad-expected.txt
new file mode 100644
index 0000000..550d85713
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/user-activation-v2/user-activation-v2/full-screen-gamepad-expected.txt
@@ -0,0 +1,11 @@
+EXPECTED (internals.runtimeFlags.userActivationV2Enabled == 'true') OK
+EVENT(fullscreenchange)
+EXPECTED (document.fullscreenElement == '[object HTMLHtmlElement]') OK
+EVENT(fullscreenchange)
+EXPECTED (document.fullscreenElement == '[object HTMLSpanElement]') OK
+EVENT(fullscreenchange)
+EXPECTED (document.fullscreenElement == '[object HTMLHtmlElement]') OK
+EVENT(fullscreenchange)
+EXPECTED (document.fullscreenElement == 'undefined') OK
+END OF TEST
+
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 3a90358..945c74ff 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -19,6 +19,7 @@
     "color_chooser/color_chooser.mojom",
     "feature_policy/feature_policy.mojom",
     "file/file_utilities.mojom",
+    "frame/find_in_page.mojom",
     "leak_detector/leak_detector.mojom",
     "loader/prefetch_url_loader_service.mojom",
     "net/ip_address_space.mojom",
diff --git a/third_party/blink/public/mojom/frame/OWNERS b/third_party/blink/public/mojom/frame/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/frame/find_in_page.mojom b/third_party/blink/public/mojom/frame/find_in_page.mojom
new file mode 100644
index 0000000..c1ef682
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/find_in_page.mojom
@@ -0,0 +1,12 @@
+// 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 blink.mojom;
+
+interface FindInPage {
+  // Notify the frame that it is no longer the active frame in the
+  // current find session, and so it should clear its active find match
+  // (and no longer highlight it with special coloring).
+  ClearActiveFindMatch();
+};
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h
index 65642f9..ffe051a 100644
--- a/third_party/blink/public/platform/task_type.h
+++ b/third_party/blink/public/platform/task_type.h
@@ -11,6 +11,8 @@
 // This enum is used for a histogram and it should not be re-numbered.
 //
 // For the task type usage guideline, see https://bit.ly/2vMAsQ4
+//
+// When a new task type is created, use kCount value as a new value.
 enum class TaskType : unsigned {
   ///////////////////////////////////////
   // Speced tasks should use one of the following task types
@@ -163,11 +165,6 @@
   // Tasks related to animation like blinking caret or CSS animation.
   kInternalAnimation = 34,
 
-  // Tasks related to accessbility. Tasks with this type are mainly posted by:
-  // * //content/renderer/accessibility
-  // * //third_party/blink/renderer/modules/accessibility
-  kInternalAccessibility = 35,
-
   ///////////////////////////////////////
   // The following task types are DEPRECATED! Use kInternal* instead.
   ///////////////////////////////////////
diff --git a/third_party/blink/public/platform/web_compositor_support.h b/third_party/blink/public/platform/web_compositor_support.h
index 890cfad..6fd2c79 100644
--- a/third_party/blink/public/platform/web_compositor_support.h
+++ b/third_party/blink/public/platform/web_compositor_support.h
@@ -37,13 +37,11 @@
 namespace cc {
 class ContentLayerClient;
 class Layer;
-class TextureLayerClient;
 }
 
 namespace blink {
 
 class WebContentLayer;
-class WebExternalTextureLayer;
 class WebImageLayer;
 class WebLayer;
 class WebScrollbarLayer;
@@ -60,9 +58,6 @@
   virtual std::unique_ptr<WebContentLayer> CreateContentLayer(
       cc::ContentLayerClient*) = 0;
 
-  virtual std::unique_ptr<WebExternalTextureLayer> CreateExternalTextureLayer(
-      cc::TextureLayerClient*) = 0;
-
   virtual std::unique_ptr<WebImageLayer> CreateImageLayer() = 0;
 
   virtual std::unique_ptr<WebScrollbarLayer> CreateScrollbarLayer(
diff --git a/third_party/blink/public/platform/web_external_texture_layer.h b/third_party/blink/public/platform/web_external_texture_layer.h
index 445dacf..72ad5a28 100644
--- a/third_party/blink/public/platform/web_external_texture_layer.h
+++ b/third_party/blink/public/platform/web_external_texture_layer.h
@@ -27,10 +27,12 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_EXTERNAL_TEXTURE_LAYER_H_
 
 #include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_float_point.h"
-#include "third_party/blink/public/platform/web_float_rect.h"
 #include "third_party/blink/public/platform/web_layer.h"
 
+namespace gfx {
+class PointF;
+}
+
 namespace blink {
 
 // This class represents a layer that renders a texture that is generated
@@ -68,8 +70,8 @@
   virtual void SetNearestNeighbor(bool) = 0;
 
   // Sets a UV transform to be used at draw time. Defaults to (0, 0) and (1, 1).
-  virtual void SetUV(const WebFloatPoint left_top,
-                     const WebFloatPoint right_bottom) = 0;
+  virtual void SetUV(const gfx::PointF& left_top,
+                     const gfx::PointF& right_bottom) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_media_player.h b/third_party/blink/public/platform/web_media_player.h
index 0e6a4bff0..3ccb19a 100644
--- a/third_party/blink/public/platform/web_media_player.h
+++ b/third_party/blink/public/platform/web_media_player.h
@@ -142,6 +142,8 @@
   virtual void RequestRemotePlaybackControl() {}
   virtual void RequestRemotePlaybackStop() {}
   virtual void RequestRemotePlaybackDisabled(bool disabled) {}
+  virtual void FlingingStarted() {}
+  virtual void FlingingStopped() {}
   virtual void SetPreload(Preload) {}
   virtual WebTimeRanges Buffered() const = 0;
   virtual WebTimeRanges Seekable() const = 0;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 991e55c..fc0f83c2 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -52,7 +52,6 @@
 class WebString;
 class WebTextCheckClient;
 class WebURL;
-class WebURLLoaderFactory;
 class WebView;
 enum class WebTreeScopeType;
 struct WebAssociatedURLLoaderOptions;
@@ -198,16 +197,6 @@
   // Note: this may lead to the destruction of the frame.
   virtual bool DispatchBeforeUnloadEvent(bool is_reload) = 0;
 
-  // Returns a WebURLRequest corresponding to the load of the WebHistoryItem.
-  virtual WebURLRequest RequestFromHistoryItem(const WebHistoryItem&,
-                                               mojom::FetchCacheMode) const = 0;
-
-  // Returns a WebURLRequest corresponding to the reload of the current
-  // HistoryItem.
-  virtual WebURLRequest RequestForReload(
-      WebFrameLoadType,
-      const WebURL& override_url = WebURL()) const = 0;
-
   // Load the given URL. For history navigations, a valid WebHistoryItem
   // should be given, as well as a WebHistoryLoadType.
   virtual void Load(
@@ -726,9 +715,6 @@
   // default behavior will be restored.
   virtual void SetTickmarks(const WebVector<WebRect>&) = 0;
 
-  // Clears the active find match in the frame, if one exists.
-  virtual void ClearActiveFindMatch() = 0;
-
   // Context menu -----------------------------------------------------------
 
   // Returns the node that the context menu opened over.
@@ -775,10 +761,6 @@
 
   // Loading ------------------------------------------------------------------
 
-  // Creates and returns a new WebURLLoaderFactory. This function can be called
-  // only when this frame is attached to a document.
-  virtual std::unique_ptr<WebURLLoaderFactory> CreateURLLoaderFactory() = 0;
-
   // Returns an AssociatedURLLoader that is associated with this frame.  The
   // loader will, for example, be cancelled when WebFrame::stopLoading is
   // called.
@@ -788,15 +770,10 @@
       const WebAssociatedURLLoaderOptions&) = 0;
 
   // Reload the current document.
-  // Note: reload() and reloadWithOverrideURL() will be deprecated.
+  // Note: reload() will be deprecated.
   // Do not use these APIs any more, but use loadRequest() instead.
   virtual void Reload(WebFrameLoadType) = 0;
 
-  // This is used for situations where we want to reload a different URL because
-  // of a redirect.
-  virtual void ReloadWithOverrideURL(const WebURL& override_url,
-                                     WebFrameLoadType) = 0;
-
   // Load the given URL.
   virtual void LoadRequest(const WebURLRequest&) = 0;
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index a2f5297..a73abc9 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -89,17 +89,6 @@
     return true;
   return false;
 }
-
-// Returns true for image elements that violate at least one of the feature
-// policy optimized image policies.
-// https://github.com/WICG/feature-policy/blob/gh-pages/policies/optimized-images.md
-bool ShouldInvertColor(const Element* element) {
-  if (IsHTMLImageElement(element)) {
-    return ToHTMLImageElement(element)->ShouldInvertColor();
-  }
-  return false;
-}
-
 }  // namespace
 
 static EDisplay EquivalentBlockDisplay(EDisplay display) {
@@ -751,15 +740,5 @@
         style.SetLogicalHeight(Length(LayoutReplaced::kDefaultHeight, kFixed));
     }
   }
-
-  // If an image element violates feature policy optimized image policies,
-  // render it with inverted color.
-  if (ShouldInvertColor(element)) {
-    FilterOperations operations;
-    operations.Operations().push_back(
-        BasicComponentTransferFilterOperation::Create(1,
-                                                      FilterOperation::INVERT));
-    style.SetFilter(operations);
-  }
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index c4b06fe2..833398da 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -457,7 +457,7 @@
 static inline bool IsValidElementNamePerHTMLParser(const CharType* characters,
                                                    unsigned length) {
   CharType c = characters[0] | 0x20;
-  if (!('a' <= c && c < 'z'))
+  if (!('a' <= c && c <= 'z'))
     return false;
 
   for (unsigned i = 1; i < length; ++i) {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 56d2c2d..66f3d76 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -150,6 +150,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
 #include "third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
 #include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h"
@@ -629,6 +630,7 @@
 }
 
 void Element::CallDistributeScroll(ScrollState& scroll_state) {
+  TRACE_EVENT0("input", "Element::CallDistributeScroll");
   ScrollStateCallback* callback =
       GetScrollCustomizationCallbacks().GetDistributeScroll(this);
 
@@ -713,6 +715,7 @@
 };
 
 void Element::CallApplyScroll(ScrollState& scroll_state) {
+  TRACE_EVENT0("input", "Element::CallApplyScroll");
   // Hits ASSERTs when trying to determine whether we need to scroll on main
   // or CC. http://crbug.com/625676.
   DisableCompositingQueryAsserts disabler;
diff --git a/third_party/blink/renderer/core/editing/BUILD.gn b/third_party/blink/renderer/core/editing/BUILD.gn
index ff6e80f0..2db2e5e 100644
--- a/third_party/blink/renderer/core/editing/BUILD.gn
+++ b/third_party/blink/renderer/core/editing/BUILD.gn
@@ -351,6 +351,7 @@
     "frame_caret_test.cc",
     "frame_selection_test.cc",
     "granularity_strategy_test.cc",
+    "hit_testing_bidi_test.cc",
     "ime/ime_text_span_test.cc",
     "ime/input_method_controller_test.cc",
     "inline_box_position_test.cc",
diff --git a/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc b/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc
new file mode 100644
index 0000000..302ef02c
--- /dev/null
+++ b/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc
@@ -0,0 +1,4022 @@
+// 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 "third_party/blink/renderer/core/dom/document.h"
+
+#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
+#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
+#include "third_party/blink/renderer/core/editing/text_affinity.h"
+
+namespace blink {
+
+class HitTestingBidiTest : public EditingTestBase {};
+
+// This file contains script-generated tests for PositionForPoint()
+// that are related to bidirectional text. The test cases are only for
+// behavior recording purposes, and do not necessarily reflect the
+// correct/desired behavior.
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual: |C B A d e f
+  // Bidi:    1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">|ABC</bdo>def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual: |C B A d e f
+  // Bidi:    1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">|ABC</bdo>def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f C B A|
+  // Bidi:    0 0 0 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def<bdo dir=\"rtl\">ABC|</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f C B A|
+  // Bidi:    0 0 0 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def<bdo dir=\"rtl\">ABC|</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOfOneRun) {
+  // Visual: |C B A
+  // Bidi:    1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">|ABC</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOfOneRun) {
+  // Visual: |C B A
+  // Bidi:    1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">|ABC</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|
+  // Bidi:    1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">ABC|</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|
+  // Bidi:    1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">ABC|</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f|C B A g h i
+  // Bidi:    0 0 0 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def|<bdo dir=\"rtl\">ABC</bdo>ghi</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f|C B A g h i
+  // Bidi:    0 0 0 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def<bdo dir=\"rtl\">|ABC</bdo>ghi</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  g h i C B A|d e f
+  // Bidi:    0 0 0 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>ghi<bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">ghi<bdo dir=\"rtl\">ABC|</bdo>def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  g h i C B A|d e f
+  // Bidi:    0 0 0 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>ghi<bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">ghi<bdo dir=\"rtl\">ABC</bdo>|def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOfOneRun) {
+  // Visual:  d e f|C B A
+  // Bidi:    0 0 0 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def|<bdo dir=\"rtl\">ABC</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockLtrBaseRunRightSideOfLeftEdgeOfOneRun) {
+  // Visual:  d e f|C B A
+  // Bidi:    0 0 0 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr>def<bdo dir=rtl>ABC</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\">def<bdo dir=\"rtl\">|ABC</bdo></div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockLtrBaseRunLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|d e f
+  // Bidi:    1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">ABC|</bdo>def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockLtrBaseRunRightSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|d e f
+  // Bidi:    1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent("<div dir=ltr><bdo dir=rtl>ABC</bdo>def</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ("<div dir=\"ltr\"><bdo dir=\"rtl\">ABC</bdo>|def</div>",
+            GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D|a b c I H G
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo "
+      "dir=\"ltr\">|abc</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D|a b c I H G
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo "
+      "dir=\"ltr\">|abc</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  I H G a b c|F E D
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  I H G a b c|F E D
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOfOneRun) {
+  // Visual:  F E D|a b c
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">|abc</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockRtlBaseRunRightSideOfLeftEdgeOfOneRun) {
+  // Visual:  F E D|a b c
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">|abc</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockRtlBaseRunLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InLtrBlockRtlBaseRunRightSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual: |a b c F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual: |a b c F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D a b c|
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D a b c|
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOfOneRun) {
+  // Visual: |a b c
+  // Bidi:    2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOfOneRun) {
+  // Visual: |a b c
+  // Bidi:    2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|
+  // Bidi:    2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|
+  // Bidi:    2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f|C B A g h i
+  // Bidi:    2 2 2 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo>ghi|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  d e f|C B A g h i
+  // Bidi:    2 2 2 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo>ghi|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  g h i C B A|d e f
+  // Bidi:    2 2 2 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">ABC</bdo>def|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  g h i C B A|d e f
+  // Bidi:    2 2 2 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">ABC</bdo>def|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOfOneRun) {
+  // Visual:  d e f|C B A
+  // Bidi:    2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockLtrBaseRunRightSideOfLeftEdgeOfOneRun) {
+  // Visual:  d e f|C B A
+  // Bidi:    2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockLtrBaseRunLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|d e f
+  // Bidi:    3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockLtrBaseRunRightSideOfRightEdgeOfOneRun) {
+  // Visual:  C B A|d e f
+  // Bidi:    3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D|a b c I H G
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  F E D|a b c I H G
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  I H G a b c|F E D
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOfOneRunWithBaseRunEnd) {
+  // Visual:  I H G a b c|F E D
+  // Bidi:    1 1 1 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOfOneRun) {
+  // Visual:  F E D|a b c
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockRtlBaseRunRightSideOfLeftEdgeOfOneRun) {
+  // Visual:  F E D|a b c
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockRtlBaseRunLeftSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest, InRtlBlockRtlBaseRunRightSideOfRightEdgeOfOneRun) {
+  // Visual:  a b c|F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i
+  // Bidi:    2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i
+  // Bidi:    2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i F E D a b c|
+  // Bidi:    0 0 0 1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i F E D a b c|
+  // Bidi:    0 0 0 1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual: |a b c F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual: |a b c F E D
+  // Bidi:    2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|
+  // Bidi:    1 1 1 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i|a b c F E D j k l
+  // Bidi:    0 0 0 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi|<bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i|a b c F E D j k l
+  // Bidi:    0 0 0 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi<bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l F E D a b c|g h i
+  // Bidi:    0 0 0 1 1 1 2 2 2 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo>ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l F E D a b c|g h i
+  // Bidi:    0 0 0 1 1 1 2 2 2 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo>|ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  g h i|a b c F E D
+  // Bidi:    0 0 0 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi|<bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  g h i|a b c F E D
+  // Bidi:    0 0 0 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>ghi<bdo dir=rtl>DEF<bdo dir=ltr>abc</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">ghi<bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|g h i
+  // Bidi:    1 1 1 2 2 2 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo>ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|g h i
+  // Bidi:    1 1 1 2 2 2 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>abc</bdo>DEF</bdo>ghi</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo>|ghi</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G|C B A d e f L K J
+  // Bidi:    1 1 1 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>|def</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G|C B A d e f L K J
+  // Bidi:    1 1 1 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J d e f C B A|I H G
+  // Bidi:    1 1 1 2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J d e f C B A|I H G
+  // Bidi:    1 1 1 2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def|<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  I H G|C B A d e f
+  // Bidi:    1 1 1 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>|def</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  I H G|C B A d e f
+  // Bidi:    1 1 1 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|I H G
+  // Bidi:    2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|I H G
+  // Bidi:    2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def|<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G
+  // Bidi:    3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G
+  // Bidi:    3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual: |C B A d e f
+  // Bidi:    3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual: |C B A d e f
+  // Bidi:    3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|
+  // Bidi:    2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|
+  // Bidi:    2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i|a b c F E D j k l
+  // Bidi:    2 2 2 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc</bdo></bdo>jkl|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  g h i|a b c F E D j k l
+  // Bidi:    2 2 2 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc</bdo></bdo>jkl|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l F E D a b c|g h i
+  // Bidi:    2 2 2 3 3 3 4 4 4 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo>ghi|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l F E D a b c|g h i
+  // Bidi:    2 2 2 3 3 3 4 4 4 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo>ghi|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  g h i|a b c F E D
+  // Bidi:    2 2 2 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">DEF|<bdo dir=\"ltr\">abc</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  g h i|a b c F E D
+  // Bidi:    2 2 2 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\">DEF|<bdo dir=\"ltr\">abc</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|g h i
+  // Bidi:    3 3 3 4 4 4 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF|</bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  F E D a b c|g h i
+  // Bidi:    3 3 3 4 4 4 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF|</bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G|C B A d e f L K J
+  // Bidi:    1 1 1 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  I H G|C B A d e f L K J
+  // Bidi:    1 1 1 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J d e f C B A|I H G
+  // Bidi:    1 1 1 2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOftwoNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J d e f C B A|I H G
+  // Bidi:    1 1 1 2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  I H G|C B A d e f
+  // Bidi:    1 1 1 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOftwoNestedRuns) {
+  // Visual:  I H G|C B A d e f
+  // Bidi:    1 1 1 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo>GHI|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|I H G
+  // Bidi:    2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 57;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOftwoNestedRuns) {
+  // Visual:  d e f C B A|I H G
+  // Bidi:    2 2 2 3 3 3 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>GHI<bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 63;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G j k l
+  // Bidi:    3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G j k l
+  // Bidi:    3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l I H G d e f C B A|
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l I H G d e f C B A|
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual: |C B A d e f I H G
+  // Bidi:    3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual: |C B A d e f I H G
+  // Bidi:    3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l|C B A d e f I H G m n o
+  // Bidi:    0 0 0 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl|<bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l|C B A d e f I H G m n o
+  // Bidi:    0 0 0 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o I H G d e f C B A|j k l
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockLtrBaseRunRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o I H G d e f C B A|j k l
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo>|jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  j k l|C B A d e f I H G
+  // Bidi:    0 0 0 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl|<bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  j k l|C B A d e f I H G
+  // Bidi:    0 0 0 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>jkl<bdo dir=rtl>GHI<bdo dir=ltr><bdo "
+      "dir=rtl>ABC</bdo>def</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">jkl<bdo dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|j k l
+  // Bidi:    1 1 1 2 2 2 3 3 3 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo>jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|j k l
+  // Bidi:    1 1 1 2 2 2 3 3 3 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>def<bdo "
+      "dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo>|jkl</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J|a b c F E D g h i O N M
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>|ghi</bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J|a b c F E D g h i O N M
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M g h i F E D a b c|L K J
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockRtlBaseRunRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M g h i F E D a b c|L K J
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi|<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  L K J|a b c F E D g h i
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>|ghi</bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  L K J|a b c F E D g h i
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|L K J
+  // Bidi:    2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|L K J
+  // Bidi:    2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi|<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i L K J
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc|</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i L K J
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc|</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J g h i F E D a b c|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J g h i F E D a b c|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual: |a b c F E D g h i
+  // Bidi:    4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc|</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual: |a b c F E D g h i
+  // Bidi:    4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc|</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|
+  // Bidi:    2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|
+  // Bidi:    2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l|C B A d e f I H G m n o
+  // Bidi:    2 2 2 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>mno|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  j k l|C B A d e f I H G m n o
+  // Bidi:    2 2 2 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>mno|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o I H G d e f C B A|j k l
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo>jkl|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockLtrBaseRunRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o I H G d e f C B A|j k l
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo>jkl|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  j k l|C B A d e f I H G
+  // Bidi:    2 2 2 5 5 5 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\">GHI|<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  j k l|C B A d e f I H G
+  // Bidi:    2 2 2 5 5 5 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\">GHI|<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|j k l
+  // Bidi:    3 3 3 4 4 4 5 5 5 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI|</bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  I H G d e f C B A|j k l
+  // Bidi:    3 3 3 4 4 4 5 5 5 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI|</bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J|a b c F E D g h i O N M
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  L K J|a b c F E D g h i O N M
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M g h i F E D a b c|L K J
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockRtlBaseRunRightSideOfRightEdgeOfthreeNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M g h i F E D a b c|L K J
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc</bdo>DEF</bdo></bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  L K J|a b c F E D g h i
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOfthreeNestedRuns) {
+  // Visual:  L K J|a b c F E D g h i
+  // Bidi:    1 1 1 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo>JKL|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|L K J
+  // Bidi:    2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 87;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOfthreeNestedRuns) {
+  // Visual:  g h i F E D a b c|L K J
+  // Bidi:    2 2 2 3 3 3 4 4 4 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>JKL<bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 93;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i L K J m n o
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual: |a b c F E D g h i L K J m n o
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o L K J g h i F E D a b c|
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InLtrBlockAtLineBoundaryRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o L K J g h i F E D a b c|
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual: |a b c F E D g h i L K J
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual: |a b c F E D g h i L K J
+  // Bidi:    4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockAtLineBoundaryRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o|a b c F E D g h i L K J p q r
+  // Bidi:    0 0 0 4 4 4 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>pqr</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno|<bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo>pqr</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o|a b c F E D g h i L K J p q r
+  // Bidi:    0 0 0 4 4 4 3 3 3 2 2 2 1 1 1 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>pqr</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo>pqr</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  p q r L K J g h i F E D a b c|m n o
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>pqr<bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">pqr<bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  p q r L K J g h i F E D a b c|m n o
+  // Bidi:    0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>pqr<bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">pqr<bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL</bdo>|mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  m n o|a b c F E D g h i L K J
+  // Bidi:    0 0 0 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno|<bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  m n o|a b c F E D g h i L K J
+  // Bidi:    0 0 0 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr>mno<bdo dir=rtl>JKL<bdo dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\">mno<bdo dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">DEF<bdo dir=\"ltr\">|abc</bdo></bdo>ghi</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|m n o
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc|</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockLtrBaseRunRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|m n o
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 0 0 0
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo "
+      "dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL</bdo>|mno</div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M|C B A d e f I H G j k l R Q P
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>PQR<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">PQR<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>|jkl</bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M|C B A d e f I H G j k l R Q P
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>PQR<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">PQR<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  R Q P j k l I H G d e f C B A|O N M
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  R Q P j k l I H G d e f C B A|O N M
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl|<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  O N M|C B A d e f I H G j k l
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>|jkl</bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  O N M|C B A d e f I H G j k l
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">|ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|O N M
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InLtrBlockRtlBaseRunRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|O N M
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=ltr><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"ltr\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl|<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G j k l O N M
+  // Bidi:    5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual: |C B A d e f I H G j k l O N M
+  // Bidi:    5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M j k l I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo></bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(
+    HitTestingBidiTest,
+    InRtlBlockAtLineBoundaryRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M j k l I H G d e f C B A|
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo></bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual: |C B A d e f I H G j k l
+  // Bidi:    5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() - 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual: |C B A d e f I H G j k l
+  // Bidi:    5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 3;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC|</bdo>def</bdo></bdo>jkl</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockAtLineBoundaryRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o|a b c F E D g h i L K J p q r
+  // Bidi:    2 2 2 6 6 6 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl>JKL<bdo "
+      "dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>pqr</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo>pqr|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  m n o|a b c F E D g h i L K J p q r
+  // Bidi:    2 2 2 6 6 6 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl>JKL<bdo "
+      "dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo>pqr</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\">JKL<bdo dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo>pqr|</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  p q r L K J g h i F E D a b c|m n o
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>pqr<bdo dir=rtl><bdo "
+      "dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">pqr<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">|abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  p q r L K J g h i F E D a b c|m n o
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>pqr<bdo dir=rtl><bdo "
+      "dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">pqr<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL|</bdo>mno</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  m n o|a b c F E D g h i L K J
+  // Bidi:    2 2 2 6 6 6 5 5 5 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl>JKL<bdo "
+      "dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\">JKL|<bdo dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  m n o|a b c F E D g h i L K J
+  // Bidi:    2 2 2 6 6 6 5 5 5 4 4 4 3 3 3
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr>mno<bdo dir=rtl>JKL<bdo "
+      "dir=ltr><bdo dir=rtl>DEF<bdo "
+      "dir=ltr>abc</bdo></bdo>ghi</bdo></bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\">mno<bdo "
+      "dir=\"rtl\">JKL|<bdo dir=\"ltr\"><bdo dir=\"rtl\">DEF<bdo "
+      "dir=\"ltr\">abc</bdo></bdo>ghi</bdo></bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|m n o
+  // Bidi:    3 3 3 4 4 4 5 5 5 6 6 6 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL|</bdo>mno</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockLtrBaseRunRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  L K J g h i F E D a b c|m n o
+  // Bidi:    3 3 3 4 4 4 5 5 5 6 6 6 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl><bdo "
+      "dir=ltr>ghi<bdo dir=rtl><bdo "
+      "dir=ltr>abc</bdo>DEF</bdo></bdo>JKL</bdo>mno</bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">ghi<bdo dir=\"rtl\"><bdo "
+      "dir=\"ltr\">abc</bdo>DEF</bdo></bdo>JKL|</bdo>mno</bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M|C B A d e f I H G j k l R Q P
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>PQR<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">PQR<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>jkl</bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  O N M|C B A d e f I H G j k l R Q P
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>PQR<bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">PQR<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>jkl</bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  R Q P j k l I H G d e f C B A|O N M
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 147;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC</bdo></bdo>GHI</bdo></bdo>|PQR</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOffourNestedRunsWithBaseRunEnd) {
+  // Visual:  R Q P j k l I H G d e f C B A|O N M
+  // Bidi:    1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 153;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo>PQR</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  O N M|C B A d e f I H G j k l
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 27;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>jkl</bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfLeftEdgeOffourNestedRuns) {
+  // Visual:  O N M|C B A d e f I H G j k l
+  // Bidi:    1 1 1 5 5 5 4 4 4 3 3 3 2 2 2
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl><bdo dir=ltr><bdo dir=rtl>GHI<bdo "
+      "dir=ltr><bdo dir=rtl>ABC</bdo>def</bdo></bdo>jkl</bdo>MNO</bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 33;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\"><bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">GHI<bdo dir=\"ltr\"><bdo "
+      "dir=\"rtl\">ABC</bdo>def</bdo></bdo>jkl</bdo>MNO|</bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunLeftSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|O N M
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 117;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+TEST_F(HitTestingBidiTest,
+       InRtlBlockRtlBaseRunRightSideOfRightEdgeOffourNestedRuns) {
+  // Visual:  j k l I H G d e f C B A|O N M
+  // Bidi:    2 2 2 3 3 3 4 4 4 5 5 5 1 1 1
+  LoadAhem();
+  InsertStyleElement("div {font: 10px/10px Ahem; width: 300px}");
+  SetBodyContent(
+      "<div dir=rtl><bdo dir=rtl>MNO<bdo dir=ltr>jkl<bdo dir=rtl><bdo "
+      "dir=ltr>def<bdo dir=rtl>ABC</bdo></bdo>GHI</bdo></bdo></bdo></div>");
+  Element* div = GetDocument().QuerySelector("div");
+  int x = div->OffsetLeft() + 123;
+  int y = div->OffsetTop() + 5;
+  const EphemeralRange result(GetDocument().caretRangeFromPoint(x, y));
+  EXPECT_TRUE(result.IsNotNull());
+  EXPECT_TRUE(result.IsCollapsed());
+  EXPECT_EQ(
+      "<div dir=\"rtl\"><bdo dir=\"rtl\">MNO<bdo dir=\"ltr\">jkl<bdo "
+      "dir=\"rtl\"><bdo dir=\"ltr\">def<bdo "
+      "dir=\"rtl\">ABC|</bdo></bdo>GHI</bdo></bdo></bdo></div>",
+      GetCaretTextFromBody(result.StartPosition()));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/iterators/character_iterator.cc b/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
index fd957f4..d07c56d 100644
--- a/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
@@ -90,37 +90,13 @@
 template <typename Strategy>
 PositionTemplate<Strategy>
 CharacterIteratorAlgorithm<Strategy>::GetPositionBefore() const {
-  if (text_iterator_.AtEnd()) {
-    DCHECK_EQ(run_offset_, 0);
-    return PositionTemplate<Strategy>(
-        text_iterator_.CurrentContainer(),
-        text_iterator_.StartOffsetInCurrentContainer());
-  }
-  const Node& node = *text_iterator_.GetNode();
-  DCHECK_GE(text_iterator_.length(), 1);
-  if (node.IsTextNode()) {
-    const int offset = text_iterator_.StartOffsetInCurrentContainer();
-    return PositionTemplate<Strategy>(node, offset + run_offset_);
-  }
-  return PositionTemplate<Strategy>::BeforeNode(node);
+  return text_iterator_.GetPositionBefore(run_offset_);
 }
 
 template <typename Strategy>
 PositionTemplate<Strategy>
 CharacterIteratorAlgorithm<Strategy>::GetPositionAfter() const {
-  if (text_iterator_.AtEnd()) {
-    DCHECK_EQ(run_offset_, 0);
-    return PositionTemplate<Strategy>(
-        text_iterator_.CurrentContainer(),
-        text_iterator_.EndOffsetInCurrentContainer());
-  }
-  DCHECK_GE(text_iterator_.length(), 1);
-  const Node& node = *text_iterator_.GetNode();
-  if (node.IsTextNode()) {
-    const int offset = text_iterator_.StartOffsetInCurrentContainer();
-    return PositionTemplate<Strategy>(node, offset + run_offset_ + 1);
-  }
-  return PositionTemplate<Strategy>::AfterNode(node);
+  return text_iterator_.GetPositionAfter(run_offset_);
 }
 
 template <typename Strategy>
diff --git a/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc b/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
index 3022d0e..212c2e6d 100644
--- a/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
+++ b/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
@@ -72,6 +72,107 @@
   EXPECT_EQ(Position(text_node, 3), result.EndPosition());
 }
 
+TEST_F(CharacterIteratorTest, GetPositionWithBlock) {
+  SetBodyContent("a<div>b</div>c");
+
+  const Element& body = *GetDocument().body();
+  CharacterIterator it(EphemeralRange::RangeOfContents(body));
+
+  const Node& text_a = *body.firstChild();
+  const Node& div = *text_a.nextSibling();
+  const Node& text_b = *div.firstChild();
+  const Node& text_c = *body.lastChild();
+
+  EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_a, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_a, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position::BeforeNode(div), it.GetPositionBefore());
+  EXPECT_EQ(Position::BeforeNode(div), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 1), it.StartPosition());
+  EXPECT_EQ(Position(body, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_b, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_b, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(div, 1), it.StartPosition());
+  EXPECT_EQ(Position(div, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_c, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_c, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_c, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(body, 3), it.GetPositionBefore());
+  EXPECT_EQ(Position(body, 3), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 3), it.StartPosition());
+  EXPECT_EQ(Position(body, 3), it.EndPosition());
+
+  EXPECT_TRUE(it.AtEnd());
+}
+
+TEST_F(CharacterIteratorTest, GetPositionWithBlocks) {
+  SetBodyContent("<p id=a>b</p><p id=c>d</p>");
+
+  const Element& body = *GetDocument().body();
+  CharacterIterator it(EphemeralRange::RangeOfContents(body));
+
+  const Node& element_p_a = *GetDocument().getElementById("a");
+  const Node& text_b = *element_p_a.firstChild();
+  const Node& element_p_c = *GetDocument().getElementById("c");
+  const Node& text_d = *element_p_c.firstChild();
+
+  EXPECT_EQ(Position(text_b, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_b, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_b, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(element_p_a, 1), it.StartPosition());
+  EXPECT_EQ(Position(element_p_a, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(element_p_a, 1), it.StartPosition());
+  EXPECT_EQ(Position(element_p_a, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_d, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_d, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_d, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_d, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(body, 2), it.GetPositionBefore());
+  EXPECT_EQ(Position(body, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 2), it.StartPosition());
+  EXPECT_EQ(Position(body, 2), it.EndPosition());
+
+  EXPECT_TRUE(it.AtEnd());
+}
+
 TEST_F(CharacterIteratorTest, GetPositionWithBR) {
   SetBodyContent("a<br>b");
 
@@ -111,4 +212,115 @@
   EXPECT_TRUE(it.AtEnd());
 }
 
+TEST_F(CharacterIteratorTest, GetPositionWithCollapsedWhitespaces) {
+  SetBodyContent("a <div> b </div> c");
+
+  const Element& body = *GetDocument().body();
+  CharacterIterator it(EphemeralRange::RangeOfContents(body));
+
+  const Node& text_a = *body.firstChild();
+  const Node& div = *text_a.nextSibling();
+  const Node& text_b = *div.firstChild();
+  const Node& text_c = *body.lastChild();
+
+  EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_a, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_a, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position::BeforeNode(div), it.GetPositionBefore());
+  EXPECT_EQ(Position::BeforeNode(div), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 1), it.StartPosition());
+  EXPECT_EQ(Position(body, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_b, 1), it.StartPosition());
+  EXPECT_EQ(Position(text_b, 2), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_b, 3), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_b, 3), it.GetPositionAfter());
+  EXPECT_EQ(Position(div, 1), it.StartPosition());
+  EXPECT_EQ(Position(div, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_c, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_c, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_c, 1), it.StartPosition());
+  EXPECT_EQ(Position(text_c, 2), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(body, 3), it.GetPositionBefore());
+  EXPECT_EQ(Position(body, 3), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 3), it.StartPosition());
+  EXPECT_EQ(Position(body, 3), it.EndPosition());
+
+  EXPECT_TRUE(it.AtEnd());
+}
+
+TEST_F(CharacterIteratorTest, GetPositionWithEmitChar16Before) {
+  InsertStyleElement("b { white-space: pre; }");
+  SetBodyContent("a   <b> c</b>");
+
+  const Element& body = *GetDocument().body();
+  CharacterIterator it(EphemeralRange::RangeOfContents(body));
+
+  const Node& text_a = *body.firstChild();
+  const Node& element_b = *text_a.nextSibling();
+  const Node& text_c = *element_b.firstChild();
+
+  EXPECT_EQ(Position(text_a, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_a, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_a, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_a, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_a, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_a, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_a, 1), it.StartPosition());
+  EXPECT_EQ(Position(text_a, 2), it.EndPosition());
+
+  // TODO(editing-dev): We should know why we emit a space character for
+  // "white-space: pre" element after trailing whitespace.
+  // A space character emitted by |EmitChar16Before()|.
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_c, 0), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_c, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_c, 0), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_c, 0), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_c, 1), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_c, 0), it.StartPosition());
+  EXPECT_EQ(Position(text_c, 1), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(text_c, 1), it.GetPositionBefore());
+  EXPECT_EQ(Position(text_c, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(text_c, 1), it.StartPosition());
+  EXPECT_EQ(Position(text_c, 2), it.EndPosition());
+
+  ASSERT_FALSE(it.AtEnd());
+  it.Advance(1);
+  EXPECT_EQ(Position(body, 2), it.GetPositionBefore());
+  EXPECT_EQ(Position(body, 2), it.GetPositionAfter());
+  EXPECT_EQ(Position(body, 2), it.StartPosition());
+  EXPECT_EQ(Position(body, 2), it.EndPosition());
+
+  EXPECT_TRUE(it.AtEnd());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
index 5d28508..9f42381 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -886,6 +886,64 @@
 }
 
 template <typename Strategy>
+PositionTemplate<Strategy> TextIteratorAlgorithm<Strategy>::GetPositionBefore(
+    int char16_offset) const {
+  if (AtEnd()) {
+    DCHECK_EQ(char16_offset, 0);
+    return PositionTemplate<Strategy>(CurrentContainer(),
+                                      StartOffsetInCurrentContainer());
+  }
+  DCHECK_GE(char16_offset, 0);
+  DCHECK_LT(char16_offset, length());
+  DCHECK_GE(length(), 1);
+  const Node& node = *text_state_.PositionNode();
+  if (text_state_.IsInTextNode() || text_state_.IsBeforeCharacter()) {
+    return PositionTemplate<Strategy>(
+        node, text_state_.PositionStartOffset() + char16_offset);
+  }
+  if (node.IsTextNode()) {
+    if (text_state_.IsAfterPositionNode())
+      return PositionTemplate<Strategy>(node, ToText(node).length());
+    return PositionTemplate<Strategy>(node, 0);
+  }
+  if (text_state_.IsAfterPositionNode())
+    return PositionTemplate<Strategy>::AfterNode(node);
+  DCHECK(!text_state_.IsBeforeChildren());
+  return PositionTemplate<Strategy>::BeforeNode(node);
+}
+
+template <typename Strategy>
+PositionTemplate<Strategy> TextIteratorAlgorithm<Strategy>::GetPositionAfter(
+    int char16_offset) const {
+  if (AtEnd()) {
+    DCHECK_EQ(char16_offset, 0);
+    return PositionTemplate<Strategy>(CurrentContainer(),
+                                      EndOffsetInCurrentContainer());
+  }
+  DCHECK_GE(char16_offset, 0);
+  DCHECK_LT(char16_offset, length());
+  DCHECK_GE(length(), 1);
+  const Node& node = *text_state_.PositionNode();
+  if (text_state_.IsBeforeCharacter()) {
+    return PositionTemplate<Strategy>(
+        node, text_state_.PositionStartOffset() + char16_offset);
+  }
+  if (text_state_.IsInTextNode()) {
+    return PositionTemplate<Strategy>(
+        node, text_state_.PositionStartOffset() + char16_offset + 1);
+  }
+  if (node.IsTextNode()) {
+    if (text_state_.IsBeforePositionNode())
+      return PositionTemplate<Strategy>(node, 0);
+    return PositionTemplate<Strategy>(node, ToText(node).length());
+  }
+  if (text_state_.IsBeforePositionNode())
+    return PositionTemplate<Strategy>::BeforeNode(node);
+  DCHECK(!text_state_.IsBeforeChildren());
+  return PositionTemplate<Strategy>::AfterNode(node);
+}
+
+template <typename Strategy>
 PositionTemplate<Strategy>
 TextIteratorAlgorithm<Strategy>::StartPositionInCurrentContainer() const {
   return PositionTemplate<Strategy>::EditingPositionOf(
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.h b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
index 6bf64aca..75ddebc 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
@@ -81,6 +81,12 @@
   PositionTemplate<Strategy> StartPositionInCurrentContainer() const;
   PositionTemplate<Strategy> EndPositionInCurrentContainer() const;
 
+  // Returns the position before |char16_offset| in current text run.
+  PositionTemplate<Strategy> GetPositionBefore(int char16_offset) const;
+
+  // Returns the position after |char16_offset| in current text run.
+  PositionTemplate<Strategy> GetPositionAfter(int char16_offset) const;
+
   const TextIteratorTextState& GetText() const { return text_state_; }
   int length() const { return text_state_.length(); }
   UChar CharacterAt(unsigned index) const {
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
index 7d443a32..66c79b7 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
@@ -144,6 +144,7 @@
       position_start_offset_ = node_index;
       position_end_offset_ = node_index;
       return;
+    case PositionNodeType::kBeforeCharacter:
     case PositionNodeType::kBeforeChildren:
     case PositionNodeType::kInText:
     case PositionNodeType::kNone:
@@ -191,7 +192,14 @@
 void TextIteratorTextState::EmitChar16Before(UChar code_unit,
                                              const Text& text_node,
                                              unsigned offset) {
-  SetTextNodePosition(text_node, offset, offset);
+  // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
+  // "U+00DF" to "SS". See "fast/css/case-transform.html"
+  // DCHECK_LE(offset, text_node.length());
+  position_node_type_ = PositionNodeType::kBeforeCharacter;
+  position_container_node_ = &text_node;
+  position_node_ = &text_node;
+  position_start_offset_ = offset;
+  position_end_offset_ = offset;
   PopulateStringBufferFromChar16(code_unit);
 }
 
@@ -253,7 +261,7 @@
 void TextIteratorTextState::SetTextNodePosition(const Text& text_node,
                                                 unsigned position_start_offset,
                                                 unsigned position_end_offset) {
-  DCHECK_LE(position_start_offset, position_end_offset);
+  DCHECK_LT(position_start_offset, position_end_offset);
   // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
   // "U+00DF" to "SS". See "fast/css/case-transform.html"
   // DCHECK_LE(position_end_offset, text_node.length());
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
index 8f1f040..e185b2f 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
@@ -91,6 +91,21 @@
   unsigned PositionEndOffset() const;
   const Node* PositionNode() const { return position_node_; }
   const Node* PositionContainerNode() const { return position_container_node_; }
+  bool IsAfterPositionNode() const {
+    return position_node_type_ == PositionNodeType::kAfterNode;
+  }
+  bool IsBeforePositionNode() const {
+    return position_node_type_ == PositionNodeType::kBeforeNode;
+  }
+  bool IsBeforeCharacter() const {
+    return position_node_type_ == PositionNodeType::kBeforeCharacter;
+  }
+  bool IsBeforeChildren() const {
+    return position_node_type_ == PositionNodeType::kBeforeChildren;
+  }
+  bool IsInTextNode() const {
+    return position_node_type_ == PositionNodeType::kInText;
+  }
 
   bool HasEmitted() const { return has_emitted_; }
   UChar LastCharacter() const { return last_character_; }
@@ -106,6 +121,7 @@
     kAfterNode,
     kAltText,
     kAsNode,
+    kBeforeCharacter,
     kBeforeChildren,
     kBeforeNode,
     kInText,
diff --git a/third_party/blink/renderer/core/editing/rendered_position.cc b/third_party/blink/renderer/core/editing/rendered_position.cc
index 2ce997d2..278c535 100644
--- a/third_party/blink/renderer/core/editing/rendered_position.cc
+++ b/third_party/blink/renderer/core/editing/rendered_position.cc
@@ -46,32 +46,12 @@
 
 namespace blink {
 
-RenderedPosition::RenderedPosition(const VisiblePosition& position)
-    : RenderedPosition(position.DeepEquivalent(), position.Affinity()) {}
-
 RenderedPosition::RenderedPosition(const VisiblePositionInFlatTree& position)
-    : RenderedPosition(position.DeepEquivalent(), position.Affinity()) {}
-
-// TODO(editing-dev): Stop duplicating code in the two constructors
-
-RenderedPosition::RenderedPosition(const Position& position,
-                                   TextAffinity affinity)
     : inline_box_(nullptr), offset_(0) {
   if (position.IsNull())
     return;
   InlineBoxPosition box_position =
-      ComputeInlineBoxPosition(PositionWithAffinity(position, affinity));
-  inline_box_ = box_position.inline_box;
-  offset_ = box_position.offset_in_box;
-}
-
-RenderedPosition::RenderedPosition(const PositionInFlatTree& position,
-                                   TextAffinity affinity)
-    : inline_box_(nullptr), offset_(0) {
-  if (position.IsNull())
-    return;
-  InlineBoxPosition box_position = ComputeInlineBoxPosition(
-      PositionInFlatTreeWithAffinity(position, affinity));
+      ComputeInlineBoxPosition(position.ToPositionWithAffinity());
   inline_box_ = box_position.inline_box;
   offset_ = box_position.offset_in_box;
 }
@@ -182,28 +162,28 @@
   return false;
 }
 
-Position RenderedPosition::PositionAtLeftBoundaryOfBiDiRun() const {
+PositionInFlatTree RenderedPosition::PositionAtLeftBoundaryOfBiDiRun() const {
   DCHECK(AtLeftBoundaryOfBidiRun());
 
   if (AtLeftmostOffsetInBox()) {
-    return Position::EditingPositionOf(
+    return PositionInFlatTree::EditingPositionOf(
         inline_box_->GetLineLayoutItem().GetNode(), offset_);
   }
 
-  return Position::EditingPositionOf(
+  return PositionInFlatTree::EditingPositionOf(
       NextLeafChild()->GetLineLayoutItem().GetNode(),
       NextLeafChild()->CaretLeftmostOffset());
 }
 
-Position RenderedPosition::PositionAtRightBoundaryOfBiDiRun() const {
+PositionInFlatTree RenderedPosition::PositionAtRightBoundaryOfBiDiRun() const {
   DCHECK(AtRightBoundaryOfBidiRun());
 
   if (AtRightmostOffsetInBox()) {
-    return Position::EditingPositionOf(
+    return PositionInFlatTree::EditingPositionOf(
         inline_box_->GetLineLayoutItem().GetNode(), offset_);
   }
 
-  return Position::EditingPositionOf(
+  return PositionInFlatTree::EditingPositionOf(
       PrevLeafChild()->GetLineLayoutItem().GetNode(),
       PrevLeafChild()->CaretRightmostOffset());
 }
diff --git a/third_party/blink/renderer/core/editing/rendered_position.h b/third_party/blink/renderer/core/editing/rendered_position.h
index 21bf0c4d..af4ff994 100644
--- a/third_party/blink/renderer/core/editing/rendered_position.h
+++ b/third_party/blink/renderer/core/editing/rendered_position.h
@@ -42,21 +42,19 @@
 class FrameSelection;
 struct CompositedSelection;
 
+// TODO(xiaochengh): RenderedPosition is deprecated. It's currently only used in
+// |SelectionController| for bidi adjustment. We should break this class and
+// move relevant code to |inline_box_traversal.cc|, and templatize it so that it
+// can be reused in LayoutNG.
 class CORE_EXPORT RenderedPosition {
   STACK_ALLOCATED();
 
  public:
   RenderedPosition();
-  explicit RenderedPosition(const VisiblePosition&);
   explicit RenderedPosition(const VisiblePositionInFlatTree&);
-  RenderedPosition(const Position&, TextAffinity);
-  RenderedPosition(const PositionInFlatTree&, TextAffinity);
   bool IsEquivalent(const RenderedPosition&) const;
 
   bool IsNull() const { return !inline_box_; }
-  const RootInlineBox* RootBox() const {
-    return inline_box_ ? &inline_box_->Root() : nullptr;
-  }
 
   unsigned char BidiLevelOnLeft() const;
   unsigned char BidiLevelOnRight() const;
@@ -79,8 +77,8 @@
     return AtRightBoundaryOfBidiRun(kMatchBidiLevel, bidi_level_of_run);
   }
 
-  Position PositionAtLeftBoundaryOfBiDiRun() const;
-  Position PositionAtRightBoundaryOfBiDiRun() const;
+  PositionInFlatTree PositionAtLeftBoundaryOfBiDiRun() const;
+  PositionInFlatTree PositionAtRightBoundaryOfBiDiRun() const;
 
   // TODO(editing-dev): This function doesn't use RenderedPosition
   // instance anymore. Consider moving.
diff --git a/third_party/blink/renderer/core/editing/selection_adjuster.cc b/third_party/blink/renderer/core/editing/selection_adjuster.cc
index 728e4330..af992e1 100644
--- a/third_party/blink/renderer/core/editing/selection_adjuster.cc
+++ b/third_party/blink/renderer/core/editing/selection_adjuster.cc
@@ -766,4 +766,67 @@
   return EditingBoundaryAdjuster::AdjustSelection(selection);
 }
 
+class SelectionTypeAdjuster final {
+  STATIC_ONLY(SelectionTypeAdjuster);
+
+ public:
+  template <typename Strategy>
+  static SelectionTemplate<Strategy> AdjustSelection(
+      const SelectionTemplate<Strategy>& selection) {
+    const EphemeralRangeTemplate<Strategy>& range = selection.ComputeRange();
+    const SelectionType selection_type = ComputeSelectionType(range);
+    if (selection_type == kCaretSelection) {
+      return typename SelectionTemplate<Strategy>::Builder()
+          .Collapse(PositionWithAffinityTemplate<Strategy>(
+              range.StartPosition(), selection.Affinity()))
+          .Build();
+    }
+
+    DCHECK_EQ(selection_type, kRangeSelection);
+    // "Constrain" the selection to be the smallest equivalent range of
+    // nodes. This is a somewhat arbitrary choice, but experience shows that
+    // it is useful to make to make the selection "canonical" (if only for
+    // purposes of comparing selections). This is an ideal point of the code
+    // to do this operation, since all selection changes that result in a
+    // RANGE come through here before anyone uses it.
+    // TODO(editing-dev): Consider this canonicalization is really needed.
+    const EphemeralRangeTemplate<Strategy> minimal_range(
+        MostForwardCaretPosition(range.StartPosition()),
+        MostBackwardCaretPosition(range.EndPosition()));
+    if (selection.IsBaseFirst()) {
+      return typename SelectionTemplate<Strategy>::Builder()
+          .SetAsForwardSelection(minimal_range)
+          .Build();
+    }
+    return typename SelectionTemplate<Strategy>::Builder()
+        .SetAsBackwardSelection(minimal_range)
+        .Build();
+  }
+
+ private:
+  template <typename Strategy>
+  static SelectionType ComputeSelectionType(
+      const EphemeralRangeTemplate<Strategy>& range) {
+    if (range.IsNull())
+      return kNoSelection;
+    DCHECK(!NeedsLayoutTreeUpdate(range.StartPosition())) << range;
+    if (range.IsCollapsed())
+      return kCaretSelection;
+    // TODO(editing-dev): Consider this canonicalization is really needed.
+    if (MostBackwardCaretPosition(range.StartPosition()) ==
+        MostBackwardCaretPosition(range.EndPosition()))
+      return kCaretSelection;
+    return kRangeSelection;
+  }
+};
+
+SelectionInDOMTree SelectionAdjuster::AdjustSelectionType(
+    const SelectionInDOMTree& selection) {
+  return SelectionTypeAdjuster::AdjustSelection(selection);
+}
+SelectionInFlatTree SelectionAdjuster::AdjustSelectionType(
+    const SelectionInFlatTree& selection) {
+  return SelectionTypeAdjuster::AdjustSelection(selection);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/selection_adjuster.h b/third_party/blink/renderer/core/editing/selection_adjuster.h
index f1261cf..37c31e98 100644
--- a/third_party/blink/renderer/core/editing/selection_adjuster.h
+++ b/third_party/blink/renderer/core/editing/selection_adjuster.h
@@ -33,6 +33,8 @@
       const SelectionInDOMTree&);
   static SelectionInFlatTree AdjustSelectionToAvoidCrossingEditingBoundaries(
       const SelectionInFlatTree&);
+  static SelectionInDOMTree AdjustSelectionType(const SelectionInDOMTree&);
+  static SelectionInFlatTree AdjustSelectionType(const SelectionInFlatTree&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 176dd56..74fae13e 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -788,8 +788,7 @@
             extent.LeftBoundaryOfBidiRun(base.BidiLevelOnRight()))) {
       return SelectionInFlatTree::Builder()
           .SetBaseAndExtent(
-              CreateVisiblePosition(
-                  ToPositionInFlatTree(base.PositionAtLeftBoundaryOfBiDiRun()))
+              CreateVisiblePosition(base.PositionAtLeftBoundaryOfBiDiRun())
                   .DeepEquivalent(),
               visible_extent.DeepEquivalent())
           .Build();
@@ -803,8 +802,7 @@
             extent.RightBoundaryOfBidiRun(base.BidiLevelOnLeft()))) {
       return SelectionInFlatTree::Builder()
           .SetBaseAndExtent(
-              CreateVisiblePosition(
-                  ToPositionInFlatTree(base.PositionAtRightBoundaryOfBiDiRun()))
+              CreateVisiblePosition(base.PositionAtRightBoundaryOfBiDiRun())
                   .DeepEquivalent(),
               visible_extent.DeepEquivalent())
           .Build();
@@ -818,8 +816,7 @@
     return SelectionInFlatTree::Builder()
         .SetBaseAndExtent(
             visible_base.DeepEquivalent(),
-            CreateVisiblePosition(
-                ToPositionInFlatTree(extent.PositionAtLeftBoundaryOfBiDiRun()))
+            CreateVisiblePosition(extent.PositionAtLeftBoundaryOfBiDiRun())
                 .DeepEquivalent())
         .Build();
   }
@@ -830,8 +827,7 @@
     return SelectionInFlatTree::Builder()
         .SetBaseAndExtent(
             visible_base.DeepEquivalent(),
-            CreateVisiblePosition(
-                ToPositionInFlatTree(extent.PositionAtRightBoundaryOfBiDiRun()))
+            CreateVisiblePosition(extent.PositionAtRightBoundaryOfBiDiRun())
                 .DeepEquivalent())
         .Build();
   }
diff --git a/third_party/blink/renderer/core/editing/visible_selection.cc b/third_party/blink/renderer/core/editing/visible_selection.cc
index d9d9f606..a2aa495 100644
--- a/third_party/blink/renderer/core/editing/visible_selection.cc
+++ b/third_party/blink/renderer/core/editing/visible_selection.cc
@@ -100,20 +100,6 @@
 }
 
 template <typename Strategy>
-static SelectionType ComputeSelectionType(
-    const EphemeralRangeTemplate<Strategy>& range) {
-  if (range.IsNull())
-    return kNoSelection;
-  DCHECK(!NeedsLayoutTreeUpdate(range.StartPosition())) << range;
-  if (range.IsCollapsed())
-    return kCaretSelection;
-  if (MostBackwardCaretPosition(range.StartPosition()) ==
-      MostBackwardCaretPosition(range.EndPosition()))
-    return kCaretSelection;
-  return kRangeSelection;
-}
-
-template <typename Strategy>
 VisibleSelectionTemplate<Strategy>::VisibleSelectionTemplate(
     const VisibleSelectionTemplate<Strategy>& other)
     : base_(other.base_),
@@ -268,42 +254,13 @@
   const SelectionTemplate<Strategy>& editing_adjusted_selection =
       SelectionAdjuster::AdjustSelectionToAvoidCrossingEditingBoundaries(
           shadow_adjusted_selection);
-  const EphemeralRangeTemplate<Strategy> editing_adjusted_range =
-      editing_adjusted_selection.ComputeRange();
-  // TODO(editing-dev): Implement
-  // const SelectionTemplate<Strategy>& adjusted_selection =
-  // AdjustSelectionType(editing_adjusted_range);
-  const SelectionType selection_type =
-      ComputeSelectionType(editing_adjusted_range);
-  if (selection_type == kCaretSelection) {
-    return typename SelectionTemplate<Strategy>::Builder()
-        .Collapse(PositionWithAffinityTemplate<Strategy>(
-            editing_adjusted_range.StartPosition(),
-            passed_selection.Affinity()))
-        .Build();
-  }
-
-  DCHECK_EQ(selection_type, kRangeSelection);
-  // "Constrain" the selection to be the smallest equivalent range of
-  // nodes. This is a somewhat arbitrary choice, but experience shows that
-  // it is useful to make to make the selection "canonical" (if only for
-  // purposes of comparing selections). This is an ideal point of the code
-  // to do this operation, since all selection changes that result in a
-  // RANGE come through here before anyone uses it.
-  // TODO(yosin) Canonicalizing is good, but haven't we already done it
-  // (when we set these two positions to |VisiblePosition|
-  // |DeepEquivalent()|s above)?
-  const EphemeralRangeTemplate<Strategy> range(
-      MostForwardCaretPosition(editing_adjusted_range.StartPosition()),
-      MostBackwardCaretPosition(editing_adjusted_range.EndPosition()));
-  if (canonicalized_selection.IsBaseFirst()) {
-    return typename SelectionTemplate<Strategy>::Builder()
-        .SetAsForwardSelection(range)
-        .Build();
-  }
-  return typename SelectionTemplate<Strategy>::Builder()
-      .SetAsBackwardSelection(range)
-      .Build();
+  const SelectionTemplate<Strategy>& type_adjusted_selection =
+      SelectionAdjuster::AdjustSelectionType(
+          typename SelectionTemplate<Strategy>::Builder(
+              editing_adjusted_selection)
+              .SetAffinity(passed_selection.Affinity())
+              .Build());
+  return type_adjusted_selection;
 }
 
 template <typename Strategy>
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 97e2c95f..2d3b21d 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -1062,7 +1062,7 @@
 
 std::unique_ptr<blink::WebURLLoaderFactory>
 LocalFrameClientImpl::CreateURLLoaderFactory() {
-  return web_frame_->CreateURLLoaderFactory();
+  return web_frame_->Client()->CreateURLLoaderFactory();
 }
 
 service_manager::InterfaceProvider*
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index f58b973..41407a51b 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -143,6 +143,7 @@
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/cursor.h"
 #include "third_party/blink/renderer/platform/drag_image.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -4517,47 +4518,25 @@
   }
 };
 
-TEST_P(ParameterizedWebFrameTest, ReloadWithOverrideURLPreservesState) {
-  const std::string first_url = "200-by-300.html";
-  const std::string second_url = "content-width-1000.html";
-  const std::string third_url = "very_tall_div.html";
+TEST_P(ParameterizedWebFrameTest, ReloadPreservesState) {
+  const std::string url = "200-by-300.html";
   const float kPageScaleFactor = 1.1684f;
   const int kPageWidth = 120;
   const int kPageHeight = 100;
 
-  RegisterMockedHttpURLLoad(first_url);
-  RegisterMockedHttpURLLoad(second_url);
-  RegisterMockedHttpURLLoad(third_url);
+  RegisterMockedHttpURLLoad(url);
 
   ClearScrollStateOnCommitWebFrameClient client;
   FrameTestHelpers::WebViewHelper web_view_helper;
-  web_view_helper.InitializeAndLoad(base_url_ + first_url, &client);
+  web_view_helper.InitializeAndLoad(base_url_ + url, &client);
   web_view_helper.Resize(WebSize(kPageWidth, kPageHeight));
   web_view_helper.LocalMainFrame()->SetScrollOffset(
       WebSize(kPageWidth / 4, kPageHeight / 4));
   web_view_helper.GetWebView()->SetPageScaleFactor(kPageScaleFactor);
 
   // Reload the page and end up at the same url. State should not be propagated.
-  web_view_helper.GetWebView()->MainFrameImpl()->ReloadWithOverrideURL(
-      ToKURL(base_url_ + first_url), WebFrameLoadType::kReload);
-  FrameTestHelpers::PumpPendingRequestsForFrameToLoad(
-      web_view_helper.GetWebView()->MainFrame());
-  EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().width);
-  EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().height);
-  EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor());
-
-  // Reload the page using the cache. State should not be propagated.
-  web_view_helper.GetWebView()->MainFrameImpl()->ReloadWithOverrideURL(
-      ToKURL(base_url_ + second_url), WebFrameLoadType::kReload);
-  FrameTestHelpers::PumpPendingRequestsForFrameToLoad(
-      web_view_helper.GetWebView()->MainFrame());
-  EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().width);
-  EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().height);
-  EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor());
-
-  // Reload the page while bypassing the cache. State should not be propagated.
-  web_view_helper.GetWebView()->MainFrameImpl()->ReloadWithOverrideURL(
-      ToKURL(base_url_ + third_url), WebFrameLoadType::kReloadBypassingCache);
+  web_view_helper.GetWebView()->MainFrameImpl()->Reload(
+      WebFrameLoadType::kReload);
   FrameTestHelpers::PumpPendingRequestsForFrameToLoad(
       web_view_helper.GetWebView()->MainFrame());
   EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().width);
@@ -7692,9 +7671,11 @@
   item.Initialize();
   WebURL history_url(ToKURL(base_url_ + "white-1x1.png"));
   item.SetURLString(history_url.GetString());
-  WebURLRequest request =
-      main_frame->RequestFromHistoryItem(item, mojom::FetchCacheMode::kDefault);
-  main_frame->Load(request, WebFrameLoadType::kBackForward, item,
+  HistoryItem* history_item = item;
+  ResourceRequest request =
+      history_item->GenerateResourceRequest(mojom::FetchCacheMode::kDefault);
+  main_frame->Load(WrappedResourceRequest(request),
+                   WebFrameLoadType::kBackForward, item,
                    kWebHistoryDifferentDocumentLoad, false,
                    base::UnguessableToken::Create());
 
diff --git a/third_party/blink/renderer/core/frame/find_in_page.cc b/third_party/blink/renderer/core/frame/find_in_page.cc
index da0b5f687..0447c2b 100644
--- a/third_party/blink/renderer/core/frame/find_in_page.cc
+++ b/third_party/blink/renderer/core/frame/find_in_page.cc
@@ -229,11 +229,9 @@
   }
 }
 
-void WebLocalFrameImpl::ClearActiveFindMatch() {
-  find_in_page_->ClearActiveFindMatch();
-}
-
 void FindInPage::ClearActiveFindMatch() {
+  // TODO(rakina): Do collapse selection as this currently does nothing.
+  frame_->ExecuteCommand(WebString::FromUTF8("CollapseSelection"));
   EnsureTextFinder().ClearActiveFindMatch();
 }
 
@@ -256,4 +254,13 @@
   return *text_finder_;
 }
 
+void FindInPage::BindToRequest(
+    mojom::blink::FindInPageAssociatedRequest request) {
+  binding_.Bind(std::move(request));
+}
+
+void FindInPage::Dispose() {
+  binding_.Close();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/find_in_page.h b/third_party/blink/renderer/core/frame/find_in_page.h
index 8cc54e2f..21c3e40 100644
--- a/third_party/blink/renderer/core/frame/find_in_page.h
+++ b/third_party/blink/renderer/core/frame/find_in_page.h
@@ -5,8 +5,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FIND_IN_PAGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FIND_IN_PAGE_H_
 
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
+#include "third_party/blink/public/platform/interface_registry.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
 
@@ -16,10 +20,14 @@
 struct WebFindOptions;
 struct WebFloatRect;
 
-class FindInPage : public GarbageCollected<FindInPage> {
+class FindInPage final : public GarbageCollectedFinalized<FindInPage>,
+                         public mojom::blink::FindInPage {
+  USING_PRE_FINALIZER(FindInPage, Dispose);
+
  public:
-  static FindInPage* Create(WebLocalFrameImpl& frame) {
-    return new FindInPage(frame);
+  static FindInPage* Create(WebLocalFrameImpl& frame,
+                            InterfaceRegistry* interface_registry) {
+    return new FindInPage(frame, interface_registry);
   }
 
   void RequestFind(int identifier,
@@ -48,7 +56,8 @@
 
   void SetTickmarks(const WebVector<WebRect>&);
 
-  void ClearActiveFindMatch();
+  // Clears the active find match in the frame, if one exists.
+  void ClearActiveFindMatch() override;
 
   TextFinder* GetTextFinder() const;
 
@@ -56,19 +65,31 @@
   // Otherwise creates it and then returns.
   TextFinder& EnsureTextFinder();
 
+  void BindToRequest(mojom::blink::FindInPageAssociatedRequest request);
+
+  void Dispose();
+
   void Trace(blink::Visitor* visitor) {
     visitor->Trace(text_finder_);
     visitor->Trace(frame_);
   }
 
  private:
-  FindInPage(WebLocalFrameImpl& frame) : frame_(&frame) {}
+  FindInPage(WebLocalFrameImpl& frame, InterfaceRegistry* interface_registry)
+      : frame_(&frame), binding_(this) {
+    if (!interface_registry)
+      return;
+    interface_registry->AddAssociatedInterface(WTF::BindRepeating(
+        &FindInPage::BindToRequest, WrapWeakPersistent(this)));
+  }
 
   // Will be initialized after first call to ensureTextFinder().
   Member<TextFinder> text_finder_;
 
   const Member<WebLocalFrameImpl> frame_;
 
+  mojo::AssociatedBinding<mojom::blink::FindInPage> binding_;
+
   DISALLOW_COPY_AND_ASSIGN(FindInPage);
 };
 
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index 094555f..bd7a833 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -120,10 +120,11 @@
                      const WebHistoryItem& item,
                      WebHistoryLoadType load_type,
                      mojom::FetchCacheMode cache_mode) {
-  WebURLRequest request = frame->RequestFromHistoryItem(item, cache_mode);
-  frame->Load(request, WebFrameLoadType::kBackForward, item,
-              kWebHistoryDifferentDocumentLoad, /*is_client_redirect=*/false,
-              base::UnguessableToken::Create());
+  HistoryItem* history_item = item;
+  frame->Load(
+      WrappedResourceRequest(history_item->GenerateResourceRequest(cache_mode)),
+      WebFrameLoadType::kBackForward, item, kWebHistoryDifferentDocumentLoad,
+      /*is_client_redirect=*/false, base::UnguessableToken::Create());
   PumpPendingRequestsForFrameToLoad(frame);
 }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 891ebd1..60caa00 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -319,8 +319,8 @@
     if (!loader_.GetDocumentLoader()->GetHistoryItem())
       return;
     FrameLoadRequest request = FrameLoadRequest(
-        nullptr, loader_.ResourceRequestForReload(load_type, NullURL(),
-                                                  client_redirect_policy));
+        nullptr,
+        loader_.ResourceRequestForReload(load_type, client_redirect_policy));
     request.SetClientRedirect(client_redirect_policy);
     loader_.Load(request, load_type);
   } else {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 3d889cb..66b7e9b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3400,25 +3400,36 @@
   if (!layer || layer->Client().ShouldThrottleRendering())
     return;
 
-  if (layer->DrawsContent()) {
-    // TODO(trchen): Currently the GraphicsLayer hierarchy is still built
-    // during CompositingUpdate, and we have to clear them here to ensure no
-    // extraneous layers are still attached. In future we will disable all
-    // those layer hierarchy code so we won't need this line.
-    layer->PlatformLayer()->RemoveAllChildren();
-
+  WebLayer* contents_layer = layer->ContentsLayer();
+  if (layer->DrawsContent() || contents_layer) {
     ScopedPaintChunkProperties scope(context.GetPaintController(),
                                      layer->GetPropertyTreeState(), *layer,
                                      DisplayItem::kForeignLayerWrapper);
-    RecordForeignLayer(context, *layer, DisplayItem::kForeignLayerWrapper,
-                       layer->PlatformLayer(),
-                       layer->GetOffsetFromTransformNode(),
-                       RoundedIntSize(layer->Size()));
+
+    if (layer->DrawsContent()) {
+      // TODO(trchen): Currently the GraphicsLayer hierarchy is still built
+      // during CompositingUpdate, and we have to clear them here to ensure no
+      // extraneous layers are still attached. In future we will disable all
+      // those layer hierarchy code so we won't need this line.
+      layer->PlatformLayer()->RemoveAllChildren();
+      RecordForeignLayer(context, *layer, DisplayItem::kForeignLayerWrapper,
+                         layer->PlatformLayer(),
+                         layer->GetOffsetFromTransformNode(),
+                         RoundedIntSize(layer->Size()));
+    }
+    if (contents_layer) {
+      auto position = contents_layer->GetPosition();
+      auto size = contents_layer->Bounds();
+      RecordForeignLayer(context, *layer,
+                         DisplayItem::kForeignLayerContentsWrapper,
+                         contents_layer,
+                         layer->GetOffsetFromTransformNode() +
+                             FloatSize(position.x(), position.y()),
+                         IntSize(size.width(), size.height()));
+    }
   }
 
-  // TODO(trchen): layer->ContentLayer() should be collected too,
-  // but how do we derive their state?
-
+  DCHECK(!layer->ContentsClippingMaskLayer());
   for (const auto* child : layer->Children())
     CollectDrawableLayersForLayerListRecursively(context, child);
   CollectDrawableLayersForLayerListRecursively(context, layer->MaskLayer());
@@ -5952,7 +5963,7 @@
         // requires changing the UkmMetricNames enum.
         {"Compositing", "IntersectionObservation", "Paint", "PrePaint",
          "StyleAndLayout"},
-        TimeDelta::FromSeconds(1)));
+        TimeDelta::FromSeconds(30)));
   }
   return *ukm_time_aggregator_;
 }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index dc84cbd8..9b1c5e89 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -914,22 +914,16 @@
 void WebLocalFrameImpl::Reload(WebFrameLoadType load_type) {
   // TODO(clamy): Remove this function once RenderFrame calls load for all
   // requests.
-  ReloadWithOverrideURL(NullURL(), load_type);
-}
-
-void WebLocalFrameImpl::ReloadWithOverrideURL(const WebURL& override_url,
-                                              WebFrameLoadType load_type) {
-  // TODO(clamy): Remove this function once RenderFrame calls load for all
-  // requests.
   DCHECK(GetFrame());
   DCHECK(IsReloadLoadType(static_cast<FrameLoadType>(load_type)));
-  WebURLRequest request = RequestForReload(load_type, override_url);
+  ResourceRequest request = GetFrame()->Loader().ResourceRequestForReload(
+      static_cast<FrameLoadType>(load_type));
   if (request.IsNull())
     return;
-  request.SetRequestorOrigin(
-      WebSecurityOrigin(GetFrame()->GetDocument()->GetSecurityOrigin()));
-  Load(request, load_type, WebHistoryItem(), kWebHistoryDifferentDocumentLoad,
-       false, base::UnguessableToken::Create());
+  request.SetRequestorOrigin(GetFrame()->GetDocument()->GetSecurityOrigin());
+  Load(WrappedResourceRequest(request), load_type, WebHistoryItem(),
+       kWebHistoryDifferentDocumentLoad, false,
+       base::UnguessableToken::Create());
 }
 
 void WebLocalFrameImpl::ReloadImage(const WebNode& web_node) {
@@ -1760,7 +1754,7 @@
       client_(client),
       local_frame_client_(LocalFrameClientImpl::Create(this)),
       autofill_client_(nullptr),
-      find_in_page_(FindInPage::Create(*this)),
+      find_in_page_(FindInPage::Create(*this, interface_registry)),
       input_events_scale_factor_for_emulation_(1),
       interface_registry_(interface_registry),
       input_method_controller_(*this),
@@ -2043,23 +2037,6 @@
   return GetFrame()->Loader().ShouldClose(is_reload);
 }
 
-WebURLRequest WebLocalFrameImpl::RequestFromHistoryItem(
-    const WebHistoryItem& item,
-    mojom::FetchCacheMode cache_mode) const {
-  HistoryItem* history_item = item;
-  return WrappedResourceRequest(
-      history_item->GenerateResourceRequest(cache_mode));
-}
-
-WebURLRequest WebLocalFrameImpl::RequestForReload(
-    WebFrameLoadType load_type,
-    const WebURL& override_url) const {
-  DCHECK(GetFrame());
-  ResourceRequest request = GetFrame()->Loader().ResourceRequestForReload(
-      static_cast<FrameLoadType>(load_type), override_url);
-  return WrappedResourceRequest(request);
-}
-
 void WebLocalFrameImpl::Load(
     const WebURLRequest& request,
     WebFrameLoadType web_frame_load_type,
@@ -2370,11 +2347,6 @@
   return frame_widget_;
 }
 
-std::unique_ptr<WebURLLoaderFactory>
-WebLocalFrameImpl::CreateURLLoaderFactory() {
-  return client_->CreateURLLoaderFactory();
-}
-
 void WebLocalFrameImpl::CopyImageAt(const WebPoint& pos_in_viewport) {
   HitTestResult result = HitTestResultForVisualViewportPos(pos_in_viewport);
   if (!IsHTMLCanvasElement(result.InnerNodeOrImageMapImage()) &&
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 9fccb40..1a7d04df 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -150,8 +150,6 @@
   v8::Local<v8::Context> MainWorldScriptContext() const override;
   v8::Local<v8::Object> GlobalProxy() const override;
   void Reload(WebFrameLoadType) override;
-  void ReloadWithOverrideURL(const WebURL& override_url,
-                             WebFrameLoadType) override;
   void ReloadImage(const WebNode&) override;
   void ReloadLoFiImages() override;
   void LoadRequest(const WebURLRequest&) override;
@@ -263,10 +261,6 @@
   WebFrame* FindFrameByName(const WebString& name) override;
   void SendPings(const WebURL& destination_url) override;
   bool DispatchBeforeUnloadEvent(bool) override;
-  WebURLRequest RequestFromHistoryItem(const WebHistoryItem&,
-                                       mojom::FetchCacheMode) const override;
-  WebURLRequest RequestForReload(WebFrameLoadType,
-                                 const WebURL&) const override;
   void Load(const WebURLRequest&,
             WebFrameLoadType,
             const WebHistoryItem&,
@@ -332,7 +326,6 @@
   void CopyImageAt(const WebPoint&) override;
   void SaveImageAt(const WebPoint&) override;
   void SetEngagementLevel(mojom::EngagementLevel) override;
-  void ClearActiveFindMatch() override;
   void UsageCountChromeLoadTimes(const WebString& metric) override;
   FrameScheduler* Scheduler() const override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
@@ -438,8 +431,6 @@
 
   void SetFrameWidget(WebFrameWidgetBase*);
 
-  std::unique_ptr<WebURLLoaderFactory> CreateURLLoaderFactory() override;
-
   // TODO(dcheng): Remove this and make |FrameWidget()| always return something
   // useful.
   WebFrameWidgetBase* LocalRootFrameWidget();
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 32103a6..ce400a9 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -819,17 +819,4 @@
   }
 }
 
-bool HTMLImageElement::ShouldInvertColor() const {
-  return should_invert_color_;
-}
-
-void HTMLImageElement::UpdateShouldInvertColor(bool value) {
-  if (should_invert_color_ != value) {
-    should_invert_color_ = value;
-    SetNeedsStyleRecalc(StyleChangeType::kLocalStyleChange,
-                        StyleChangeReasonForTracing::Create(
-                            StyleChangeReason::kPolicyViolation));
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h
index 19d458ac..8f8524d 100644
--- a/third_party/blink/renderer/core/html/html_image_element.h
+++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -136,12 +136,6 @@
   FormAssociated* ToFormAssociatedOrNull() override { return this; };
   void AssociateWith(HTMLFormElement*) override;
 
-  // When an image element violates feature policy optimized image policies, it
-  // should be rendered with inverted color.
-  // https://github.com/WICG/feature-policy/blob/gh-pages/policies/optimized-images.md
-  bool ShouldInvertColor() const;
-  void UpdateShouldInvertColor(bool);
-
  protected:
   // Controls how an image element appears in the layout. See:
   // https://html.spec.whatwg.org/multipage/embedded-content.html#image-request
diff --git a/third_party/blink/renderer/core/html/html_image_loader.cc b/third_party/blink/renderer/core/html/html_image_loader.cc
index f5ebd697..45bcc55 100644
--- a/third_party/blink/renderer/core/html/html_image_loader.cc
+++ b/third_party/blink/renderer/core/html/html_image_loader.cc
@@ -75,30 +75,10 @@
 
   bool load_error = cached_image->ErrorOccurred();
   if (auto* image = ToHTMLImageElementOrNull(*element)) {
-    if (load_error) {
+    if (load_error)
       image->EnsureCollapsedOrFallbackContent();
-    } else {
+    else
       image->EnsurePrimaryContent();
-      // Check policies
-      if (auto* frame = image->GetDocument().GetFrame()) {
-        // Invert the image if the document does not have the
-        // 'legacy-image-formats' feature enabled, and the image is not one of
-        // the allowed formats.
-        if (!frame->IsFeatureEnabled(
-                mojom::FeaturePolicyFeature::kLegacyImageFormats)) {
-          if (!cached_image->IsAcceptableContentType())
-            image->UpdateShouldInvertColor(true);
-        }
-        // Invert the image if the document does not have the
-        // 'image-compression' feature enabled and the image is not
-        // sufficiently-well-compressed.
-        if (!frame->IsFeatureEnabled(
-                mojom::FeaturePolicyFeature::kImageCompression)) {
-          if (!cached_image->IsAcceptableCompressionRatio())
-            image->UpdateShouldInvertColor(true);
-        }
-      }
-    }
   }
 
   if (auto* input = ToHTMLInputElementOrNull(*element)) {
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index c963902..d8ee5ccf 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -2485,6 +2485,16 @@
     GetWebMediaPlayer()->RequestRemotePlaybackStop();
 }
 
+void HTMLMediaElement::FlingingStarted() {
+  if (GetWebMediaPlayer())
+    GetWebMediaPlayer()->FlingingStarted();
+}
+
+void HTMLMediaElement::FlingingStopped() {
+  if (GetWebMediaPlayer())
+    GetWebMediaPlayer()->FlingingStopped();
+}
+
 void HTMLMediaElement::CloseMediaSource() {
   if (!media_source_)
     return;
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index cbac044..f87d77f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -192,6 +192,8 @@
   void RequestRemotePlayback();
   void RequestRemotePlaybackControl();
   void RequestRemotePlaybackStop();
+  void FlingingStarted();
+  void FlingingStopped();
 
   // statistics
   unsigned webkitAudioDecodedByteCount() const;
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index b9a98e2..9c7d8d61 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/scroll/scroll_customization.h"
 
 namespace blink {
@@ -282,6 +283,7 @@
 }
 
 void ScrollManager::CustomizedScroll(ScrollState& scroll_state) {
+  TRACE_EVENT0("input", "ScrollManager::CustomizedScroll");
   if (scroll_state.FullyConsumed())
     return;
 
@@ -356,6 +358,7 @@
 
 WebInputEventResult ScrollManager::HandleGestureScrollBegin(
     const WebGestureEvent& gesture_event) {
+  TRACE_EVENT0("input", "ScrollManager::handleGestureScrollBegin");
   Document* document = frame_->GetDocument();
 
   if (!document->GetLayoutView())
@@ -373,8 +376,11 @@
     scroll_gesture_handling_node_ = frame_->GetDocument()->documentElement();
 
   if (!scroll_gesture_handling_node_ ||
-      !scroll_gesture_handling_node_->GetLayoutObject())
+      !scroll_gesture_handling_node_->GetLayoutObject()) {
+    TRACE_EVENT_INSTANT0("input", "Dropping: No LayoutObject",
+                         TRACE_EVENT_SCOPE_THREAD);
     return WebInputEventResult::kNotHandled;
+  }
 
   WebInputEventResult child_result = PassScrollGestureEvent(
       gesture_event, scroll_gesture_handling_node_->GetLayoutObject());
@@ -399,6 +405,10 @@
   RecomputeScrollChain(*scroll_gesture_handling_node_.Get(), *scroll_state,
                        current_scroll_chain_);
 
+  TRACE_EVENT_INSTANT1("input", "Computed Scroll Chain",
+                       TRACE_EVENT_SCOPE_THREAD, "length",
+                       current_scroll_chain_.size());
+
   if (current_scroll_chain_.empty()) {
     // If a child has a non-empty scroll chain, we need to consider that instead
     // of simply returning WebInputEventResult::kNotHandled.
@@ -419,10 +429,13 @@
 
 WebInputEventResult ScrollManager::HandleGestureScrollUpdate(
     const WebGestureEvent& gesture_event) {
+  TRACE_EVENT0("input", "ScrollManager::handleGestureScrollUpdate");
   DCHECK_EQ(gesture_event.GetType(), WebInputEvent::kGestureScrollUpdate);
 
   Node* node = scroll_gesture_handling_node_.Get();
   if (!node || !node->GetLayoutObject()) {
+    TRACE_EVENT_INSTANT0("input", "Lost scroll_gesture_handling_node",
+                         TRACE_EVENT_SCOPE_THREAD);
     if (previous_gesture_scrolled_element_) {
       // When the scroll_gesture_handling_node_ gets deleted in the middle of
       // scrolling call HandleGestureScrollEvent to start scrolling a new node
@@ -432,9 +445,14 @@
           SynthesizeGestureScrollBegin(gesture_event);
       HandleGestureScrollEvent(scroll_begin);
       node = scroll_gesture_handling_node_.Get();
-      if (!node || !node->GetLayoutObject())
+      if (!node || !node->GetLayoutObject()) {
+        TRACE_EVENT_INSTANT0("input", "Failed to find new node",
+                             TRACE_EVENT_SCOPE_THREAD);
         return WebInputEventResult::kNotHandled;
+      }
     } else {
+      TRACE_EVENT_INSTANT0("input", "No previously scrolled node",
+                           TRACE_EVENT_SCOPE_THREAD);
       return WebInputEventResult::kNotHandled;
     }
   }
@@ -463,8 +481,11 @@
     return result;
   }
 
-  if (current_scroll_chain_.empty())
+  if (current_scroll_chain_.empty()) {
+    TRACE_EVENT_INSTANT0("input", "Empty Scroll Chain",
+                         TRACE_EVENT_SCOPE_THREAD);
     return WebInputEventResult::kNotHandled;
+  }
 
   std::unique_ptr<ScrollStateData> scroll_state_data =
       std::make_unique<ScrollStateData>();
@@ -538,6 +559,7 @@
 
 WebInputEventResult ScrollManager::HandleGestureScrollEnd(
     const WebGestureEvent& gesture_event) {
+  TRACE_EVENT0("input", "ScrollManager::handleGestureScrollEnd");
   Node* node = scroll_gesture_handling_node_;
 
   if (node && node->GetLayoutObject()) {
@@ -607,6 +629,8 @@
   if (!frame_->View())
     return WebInputEventResult::kNotHandled;
 
+  TRACE_EVENT0("input", "ScrollManager::handleGestureScrollEvent");
+
   Node* event_target = nullptr;
   Scrollbar* scrollbar = nullptr;
   if (gesture_event.GetType() != WebInputEvent::kGestureScrollBegin) {
@@ -619,6 +643,9 @@
     if (!document->GetLayoutView())
       return WebInputEventResult::kNotHandled;
 
+    TRACE_EVENT_INSTANT0("input", "Retargeting Scroll",
+                         TRACE_EVENT_SCOPE_THREAD);
+
     LocalFrameView* view = frame_->View();
     LayoutPoint view_point = view->RootFrameToContents(
         FlooredIntPoint(gesture_event.PositionInRootFrame()));
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 1e46e1b..1f1e3920 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -5528,6 +5528,12 @@
       # The id of the context created.
       BrowserContextID browserContextId
 
+  # Returns all browser contexts created with `Target.createBrowserContext` method.
+  experimental command getBrowserContexts
+    returns
+      # An array of browser context ids.
+      array of BrowserContextID browserContextIds
+
   # Creates a new page.
   command createTarget
     parameters
@@ -5537,7 +5543,7 @@
       optional integer width
       # Frame height in DIP (headless chrome only).
       optional integer height
-      # The browser context to create the page in (headless chrome only).
+      # The browser context to create the page in.
       optional BrowserContextID browserContextId
       # Whether BeginFrames for this target will be controlled via DevTools (headless chrome only,
       # not supported on MacOS yet, false by default).
@@ -5554,12 +5560,11 @@
       # Deprecated.
       deprecated optional TargetID targetId
 
-  # Deletes a BrowserContext, will fail of any open page uses it.
+  # Deletes a BrowserContext. All the belonging pages will be closed without calling their
+  # beforeunload hooks.
   experimental command disposeBrowserContext
     parameters
       BrowserContextID browserContextId
-    returns
-      boolean success
 
   # Returns information about a target.
   experimental command getTargetInfo
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
index 809e0f4..c5548c16 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
@@ -18,6 +18,7 @@
       // Note that the spec says the initial value of m_lastThresholdIndex
       // should be -1, but since m_lastThresholdIndex is unsigned, we use a
       // different sentinel value.
+      last_is_visible_(false),
       last_threshold_index_(kMaxThresholdIndex - 1) {
   UpdateShouldReportRootBoundsAfterDomChange();
 }
@@ -72,16 +73,24 @@
   // TODO(tkent): We can't use CHECK_LT due to a compile error.
   CHECK(new_threshold_index < kMaxThresholdIndex);
 
-  if (last_threshold_index_ != new_threshold_index) {
+  bool is_visible = false;
+  if (RuntimeEnabledFeatures::IntersectionObserverV2Enabled() &&
+      Observer()->trackVisibility()) {
+    // TODO(szager): Determine visibility.
+  }
+
+  if (last_threshold_index_ != new_threshold_index ||
+      last_is_visible_ != is_visible) {
     FloatRect snapped_root_bounds(geometry.RootRect());
     FloatRect* root_bounds_pointer =
         should_report_root_bounds_ ? &snapped_root_bounds : nullptr;
     IntersectionObserverEntry* new_entry = new IntersectionObserverEntry(
         timestamp, new_visible_ratio, FloatRect(geometry.TargetRect()),
         root_bounds_pointer, FloatRect(geometry.IntersectionRect()),
-        geometry.DoesIntersect(), Target());
+        geometry.DoesIntersect(), is_visible, Target());
     Observer()->EnqueueIntersectionObserverEntry(*new_entry);
     SetLastThresholdIndex(new_threshold_index);
+    SetWasVisible(is_visible);
   }
 }
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.h b/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
index 1217ae43..1f364014d 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
@@ -33,14 +33,17 @@
 
  private:
   void SetLastThresholdIndex(unsigned index) { last_threshold_index_ = index; }
+  void SetWasVisible(bool last_is_visible) {
+    last_is_visible_ = last_is_visible ? 1 : 0;
+  }
 
   Member<IntersectionObserver> observer_;
   WeakMember<Element> target_;
 
   unsigned should_report_root_bounds_ : 1;
-
-  unsigned last_threshold_index_ : 30;
-  static const unsigned kMaxThresholdIndex = (unsigned)0x40000000;
+  unsigned last_is_visible_ : 1;
+  unsigned last_threshold_index_ : 29;
+  static const unsigned kMaxThresholdIndex = (unsigned)0x20000000;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
index ae9fc71..e604951 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
@@ -150,7 +150,12 @@
   if (exception_state.HadException())
     return nullptr;
 
-  return new IntersectionObserver(delegate, root, root_margin, thresholds);
+  bool track_visibility = false;
+  if (RuntimeEnabledFeatures::IntersectionObserverV2Enabled())
+    track_visibility = observer_init.trackVisibility();
+
+  return new IntersectionObserver(delegate, root, root_margin, thresholds,
+                                  track_visibility);
 }
 
 IntersectionObserver* IntersectionObserver::Create(
@@ -168,18 +173,20 @@
     const Vector<float>& thresholds,
     Document* document,
     EventCallback callback,
+    bool track_visibility,
     ExceptionState& exception_state) {
   IntersectionObserverDelegateImpl* intersection_observer_delegate =
       new IntersectionObserverDelegateImpl(document, std::move(callback));
   return new IntersectionObserver(*intersection_observer_delegate, nullptr,
-                                  root_margin, thresholds);
+                                  root_margin, thresholds, track_visibility);
 }
 
 IntersectionObserver::IntersectionObserver(
     IntersectionObserverDelegate& delegate,
     Element* root,
     const Vector<Length>& root_margin,
-    const Vector<float>& thresholds)
+    const Vector<float>& thresholds,
+    bool track_visibility)
     : ContextClient(delegate.GetExecutionContext()),
       delegate_(&delegate),
       root_(root),
@@ -188,7 +195,8 @@
       right_margin_(kFixed),
       bottom_margin_(kFixed),
       left_margin_(kFixed),
-      root_is_implicit_(root ? 0 : 1) {
+      root_is_implicit_(root ? 0 : 1),
+      track_visibility_(track_visibility ? 1 : 0) {
   switch (root_margin.size()) {
     case 0:
       break;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
index 8301a33..3aca247 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
@@ -50,6 +50,7 @@
                                       const Vector<float>& thresholds,
                                       Document*,
                                       EventCallback,
+                                      bool track_visbility = false,
                                       ExceptionState& = ASSERT_NO_EXCEPTION);
   static void ResumeSuspendedObservers();
 
@@ -63,6 +64,7 @@
   Element* root() const { return root_.Get(); }
   String rootMargin() const;
   const Vector<float>& thresholds() const { return thresholds_; }
+  bool trackVisibility() const { return track_visibility_; }
 
   // An observer can either track intersections with an explicit root Element,
   // or with the the top-level frame's viewport (the "implicit root").  When
@@ -100,7 +102,8 @@
   explicit IntersectionObserver(IntersectionObserverDelegate&,
                                 Element*,
                                 const Vector<Length>& root_margin,
-                                const Vector<float>& thresholds);
+                                const Vector<float>& thresholds,
+                                bool track_visibility);
   void ClearWeakMembers(Visitor*);
 
   // Returns false if this observer has an explicit root element which has been
@@ -117,6 +120,7 @@
   Length bottom_margin_;
   Length left_margin_;
   unsigned root_is_implicit_ : 1;
+  unsigned track_visibility_ : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.idl b/third_party/blink/renderer/core/intersection_observer/intersection_observer.idl
index 180b996..fc1bd4494 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.idl
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.idl
@@ -19,6 +19,7 @@
     readonly attribute DOMString rootMargin;
     // https://github.com/WICG/IntersectionObserver/issues/114
     readonly attribute FrozenArray<double> thresholds;
+    [RuntimeEnabled=IntersectionObserverV2] readonly attribute boolean trackVisibility;
     [RaisesException] void observe(Element target);
     [RaisesException] void unobserve(Element target);
     [RaisesException] void disconnect();
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
index 2ddfe3a..7707b151 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
@@ -15,6 +15,7 @@
     const FloatRect* root_bounds,
     const FloatRect& intersection_rect,
     bool is_intersecting,
+    bool is_visible,
     Element* target)
     : time_(time),
       intersection_ratio_(intersection_ratio),
@@ -24,7 +25,8 @@
                                : nullptr),
       intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
       target_(target),
-      is_intersecting_(is_intersecting)
+      is_intersecting_(is_intersecting),
+      is_visible_(is_visible)
 
 {}
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
index 95d6bcd..0805035 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
@@ -25,6 +25,7 @@
                             const FloatRect* root_bounds,
                             const FloatRect& intersection_rect,
                             bool is_intersecting,
+                            bool is_visible,
                             Element*);
 
   double time() const { return time_; }
@@ -33,6 +34,7 @@
   DOMRectReadOnly* rootBounds() const { return root_bounds_; }
   DOMRectReadOnly* intersectionRect() const { return intersection_rect_; }
   bool isIntersecting() const { return is_intersecting_; }
+  bool isVisible() const { return is_visible_; }
   Element* target() const { return target_.Get(); }
 
   void Trace(blink::Visitor*) override;
@@ -45,6 +47,7 @@
   Member<DOMRectReadOnly> intersection_rect_;
   Member<Element> target_;
   bool is_intersecting_;
+  bool is_visible_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.idl b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.idl
index fb26cf2..ec82d861 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.idl
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.idl
@@ -11,6 +11,7 @@
     readonly attribute DOMRectReadOnly boundingClientRect;
     readonly attribute DOMRectReadOnly intersectionRect;
     readonly attribute boolean isIntersecting;
+    [RuntimeEnabled=IntersectionObserverV2] readonly attribute boolean isVisible;
     readonly attribute double intersectionRatio;
     readonly attribute Element target;
 };
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_init.idl b/third_party/blink/renderer/core/intersection_observer/intersection_observer_init.idl
index ef0de4ab..0dbb57a 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_init.idl
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_init.idl
@@ -8,4 +8,5 @@
     Element? root = null;
     DOMString rootMargin = "0px";
     (double or sequence<double>) threshold = 0;
+    [RuntimeEnabled=IntersectionObserverV2] boolean trackVisibility = false;
 };
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 426e411..332f3879 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -234,4 +235,19 @@
   EXPECT_TRUE(observer_delegate->LastIntersectionRect().IsEmpty());
 }
 
+TEST_F(IntersectionObserverTest, TrackVisibilityInit) {
+  ScopedIntersectionObserverV2ForTest iov2_enabled(true);
+  IntersectionObserverInit observer_init;
+  DummyExceptionStateForTesting exception_state;
+  TestIntersectionObserverDelegate* observer_delegate =
+      new TestIntersectionObserverDelegate(GetDocument());
+  IntersectionObserver* observer = IntersectionObserver::Create(
+      observer_init, *observer_delegate, exception_state);
+  EXPECT_FALSE(observer->trackVisibility());
+  observer_init.setTrackVisibility(true);
+  observer = IntersectionObserver::Create(observer_init, *observer_delegate,
+                                          exception_state);
+  EXPECT_TRUE(observer->trackVisibility());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 3e07e42..49b5663 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -2148,11 +2148,11 @@
        (!style.LogicalTop().IsAuto() && !style.LogicalBottom().IsAuto()));
 
   LayoutUnit stretched_flex_height(-1);
-  if (IsFlexItem())
-    stretched_flex_height =
-        ToLayoutFlexibleBox(Parent())
-            ->ChildLogicalHeightForPercentageResolution(*this);
-
+  if (IsFlexItem()) {
+    const LayoutFlexibleBox* flex_box = ToLayoutFlexibleBox(Parent());
+    if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*this))
+      stretched_flex_height = OverrideContentLogicalHeight();
+  }
   if (stretched_flex_height != LayoutUnit(-1)) {
     available_height = stretched_flex_height;
   } else if (IsGridItem() && HasOverrideLogicalHeight()) {
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 00ed0491..5ec22ee 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -51,7 +51,9 @@
 #include "third_party/blink/renderer/core/layout/line/inline_iterator.h"
 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
 #include "third_party/blink/renderer/core/layout/line/line_width.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h"
@@ -2659,6 +2661,20 @@
     return FirstLineBox()->LogicalTop() +
            font_data->GetFontMetrics().Ascent(FirstRootBox()->BaselineType());
   }
+  if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
+    if (const NGPaintFragment* paint_fragment = PaintFragment()) {
+      NGBoxFragment box_fragment(
+          StyleRef().GetWritingMode(),
+          ToNGPhysicalBoxFragment(paint_fragment->PhysicalFragment()));
+      NGLineHeightMetrics metrics =
+          box_fragment.BaselineMetricsWithoutSynthesize(
+              {NGBaselineAlgorithmType::kFirstLine,
+               IsHorizontalWritingMode() ? kAlphabeticBaseline
+                                         : kIdeographicBaseline});
+      if (!metrics.IsEmpty())
+        return metrics.ascent;
+    }
+  }
   return LayoutUnit(-1);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 3dcc368..c185a1de 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3633,9 +3633,10 @@
         LayoutBlock* block = ToLayoutBlock(cb);
         block->AddPercentHeightDescendant(const_cast<LayoutBox*>(this));
         if (block->IsFlexItem()) {
-          stretched_height =
-              ToLayoutFlexibleBox(block->Parent())
-                  ->ChildLogicalHeightForPercentageResolution(*block);
+          const LayoutFlexibleBox* flex_box =
+              ToLayoutFlexibleBox(block->Parent());
+          if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*block))
+            stretched_height = block->OverrideContentLogicalHeight();
         } else if (block->IsGridItem() && block->HasOverrideLogicalHeight() &&
                    !has_perpendicular_containing_block) {
           stretched_height = block->OverrideContentLogicalHeight();
@@ -3740,11 +3741,9 @@
   }
 
   if (IsFlexItem()) {
-    LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
-    LayoutUnit stretched_height =
-        flex_box.ChildLogicalHeightForPercentageResolution(*this);
-    if (stretched_height != LayoutUnit(-1))
-      return stretched_height;
+    const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
+    if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this))
+      return OverrideContentLogicalHeight();
   }
 
   if (h.IsPercentOrCalc() && IsOutOfFlowPositioned()) {
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index c2f6015..712392e 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -678,9 +678,8 @@
   if (logical_height_length.IsPercentOrCalc() && cb && IsBox())
     cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(ToLayoutBox(this)));
   if (this_box && this_box->IsFlexItem()) {
-    LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
-    if (flex_box.ChildLogicalHeightForPercentageResolution(*this_box) !=
-        LayoutUnit(-1))
+    const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
+    if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box))
       return false;
   }
   if (this_box && this_box->IsGridItem() &&
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 952fb88..5afeba3e 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1119,17 +1119,17 @@
   return sizes;
 }
 
-LayoutUnit LayoutFlexibleBox::CrossSizeForPercentageResolution(
-    const LayoutBox& child) {
+bool LayoutFlexibleBox::CrossSizeIsDefiniteForPercentageResolution(
+    const LayoutBox& child) const {
   if (FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child.StyleRef()) !=
       ItemPosition::kStretch)
-    return LayoutUnit(-1);
+    return false;
 
   // Here we implement https://drafts.csswg.org/css-flexbox/#algo-stretch
   if (HasOrthogonalFlow(child) && child.HasOverrideLogicalWidth())
-    return child.OverrideContentLogicalWidth();
+    return true;
   if (!HasOrthogonalFlow(child) && child.HasOverrideLogicalHeight())
-    return child.OverrideContentLogicalHeight();
+    return true;
 
   // We don't currently implement the optimization from
   // https://drafts.csswg.org/css-flexbox/#definite-sizes case 1. While that
@@ -1137,39 +1137,36 @@
   // definite size, which itself is not cheap. We can consider implementing it
   // at a later time. (The correctness is ensured by redoing layout in
   // applyStretchAlignmentToChild)
-  return LayoutUnit(-1);
+  return false;
 }
 
-LayoutUnit LayoutFlexibleBox::MainSizeForPercentageResolution(
-    const LayoutBox& child) {
+bool LayoutFlexibleBox::MainSizeIsDefiniteForPercentageResolution(
+    const LayoutBox& child) const {
   // This function implements section 9.8. Definite and Indefinite Sizes, case
   // 2) of the flexbox spec.
   // We need to check for the flexbox to have a definite main size, and for the
   // flex item to have a definite flex basis.
   const Length& flex_basis = FlexBasisForChild(child);
   if (!MainAxisLengthIsDefinite(child, flex_basis))
-    return LayoutUnit(-1);
+    return false;
   if (!flex_basis.IsPercentOrCalc()) {
     // If flex basis had a percentage, our size is guaranteed to be definite or
     // the flex item's size could not be definite. Otherwise, we make up a
     // percentage to check whether we have a definite size.
     if (!MainAxisLengthIsDefinite(child, Length(0, kPercent)))
-      return LayoutUnit(-1);
+      return false;
   }
 
   if (HasOrthogonalFlow(child))
-    return child.HasOverrideLogicalHeight()
-               ? child.OverrideContentLogicalHeight()
-               : LayoutUnit(-1);
-  return child.HasOverrideLogicalWidth() ? child.OverrideContentLogicalWidth()
-                                         : LayoutUnit(-1);
+    return child.HasOverrideLogicalHeight();
+  return child.HasOverrideLogicalWidth();
 }
 
-LayoutUnit LayoutFlexibleBox::ChildLogicalHeightForPercentageResolution(
-    const LayoutBox& child) {
+bool LayoutFlexibleBox::UseOverrideLogicalHeightForPerentageResolution(
+    const LayoutBox& child) const {
   if (!HasOrthogonalFlow(child))
-    return CrossSizeForPercentageResolution(child);
-  return MainSizeForPercentageResolution(child);
+    return CrossSizeIsDefiniteForPercentageResolution(child);
+  return MainSizeIsDefiniteForPercentageResolution(child);
 }
 
 LayoutUnit LayoutFlexibleBox::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
index 9e78323..33b1aa9 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.h
@@ -75,9 +75,14 @@
 
   const OrderIterator& GetOrderIterator() const { return order_iterator_; }
 
-  LayoutUnit CrossSizeForPercentageResolution(const LayoutBox& child);
-  LayoutUnit MainSizeForPercentageResolution(const LayoutBox& child);
-  LayoutUnit ChildLogicalHeightForPercentageResolution(const LayoutBox& child);
+  // These three functions are used when resolving percentages against a
+  // flex item's logical height. In flexbox, sometimes a logical height
+  // should be considered definite even though it normally shouldn't be,
+  // and these functions implement that logic.
+  bool CrossSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const;
+  bool MainSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const;
+  bool UseOverrideLogicalHeightForPerentageResolution(
+      const LayoutBox& child) const;
 
   void ClearCachedMainSizeForChild(const LayoutBox& child);
 
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 3b33eee..4c1bf82 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -39,17 +39,48 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
 #include "third_party/blink/renderer/core/paint/image_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
+#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h"
 
 namespace blink {
 
+namespace {
+
+void CheckForOptimizedImagePolicy(const LocalFrame& frame,
+                                  LayoutImage* layout_image,
+                                  ImageResourceContent* new_image) {
+  // Invert the image if the document does not have the 'legacy-image-formats'
+  // feature enabled, and the image is not one of the allowed formats.
+  if (IsSupportedInFeaturePolicy(
+          mojom::FeaturePolicyFeature::kLegacyImageFormats) &&
+      !frame.IsFeatureEnabled(
+          mojom::FeaturePolicyFeature::kLegacyImageFormats)) {
+    if (!new_image->IsAcceptableContentType()) {
+      layout_image->UpdateShouldInvertColor(true);
+      return;
+    }
+  }
+  // Invert the image if the document does not have the image-compression'
+  // feature enabled and the image is not sufficiently-well-compressed.
+  if (IsSupportedInFeaturePolicy(
+          mojom::FeaturePolicyFeature::kImageCompression) &&
+      !frame.IsFeatureEnabled(mojom::FeaturePolicyFeature::kImageCompression)) {
+    if (!new_image->IsAcceptableCompressionRatio())
+      layout_image->UpdateShouldInvertColor(true);
+  }
+}
+
+}  // namespace
+
 using namespace HTMLNames;
 
 LayoutImage::LayoutImage(Element* element)
     : LayoutReplaced(element, LayoutSize()),
       did_increment_visually_non_empty_pixel_count_(false),
       is_generated_content_(false),
-      image_device_pixel_ratio_(1.0f) {}
+      image_device_pixel_ratio_(1.0f),
+      should_invert_color_(false) {}
 
 LayoutImage* LayoutImage::CreateAnonymous(PseudoElement& pseudo) {
   LayoutImage* image = new LayoutImage(nullptr);
@@ -197,6 +228,12 @@
 
   InvalidateBackgroundObscurationStatus();
 
+  // Check for optimized image policies.
+  if (View() && View()->GetFrameView()) {
+    CheckForOptimizedImagePolicy(View()->GetFrameView()->GetFrame(), this,
+                                 new_image);
+  }
+
   if (new_image == image_resource_->CachedImage()) {
     // tell any potential compositing layers
     // that the image is done and they can reference it directly.
@@ -352,4 +389,18 @@
   return ToSVGImage(image)->EmbeddedReplacedContent();
 }
 
+bool LayoutImage::ShouldInvertColor() const {
+  return should_invert_color_;
+}
+
+void LayoutImage::UpdateShouldInvertColor(bool value) {
+  if (should_invert_color_ != value) {
+    should_invert_color_ = value;
+    SetNeedsPaintPropertyUpdate();
+    // If composited image, update compositing layer.
+    if (Layer())
+      Layer()->SetNeedsCompositingInputsUpdate();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h
index d2e24d9..f43982b 100644
--- a/third_party/blink/renderer/core/layout/layout_image.h
+++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -87,6 +87,12 @@
 
   const char* GetName() const override { return "LayoutImage"; }
 
+  // When an image element violates feature policy optimized image policies, it
+  // should be rendered with inverted color.
+  // https://github.com/WICG/feature-policy/blob/gh-pages/policies/optimized-images.md
+  bool ShouldInvertColor() const;
+  void UpdateShouldInvertColor(bool);
+
  protected:
   bool NeedsPreferredWidthsRecalculation() const final;
   LayoutReplaced* EmbeddedReplacedContent() const;
@@ -147,6 +153,8 @@
   // This field stores whether this image is generated with 'content'.
   bool is_generated_content_;
   float image_device_pixel_ratio_;
+
+  bool should_invert_color_;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutImage, IsLayoutImage());
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index a711c24..28acc6f2 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1460,6 +1460,10 @@
   // |ancestor| itself if |ancestor| scrolls overflow.
   // The output rect is suitable for purposes such as paint invalidation.
   //
+  // The ancestor can be nullptr which, if |this| is not the root view, will map
+  // the rect to the main frame's space which includes the root view's scroll
+  // and clip. This is even true if the main frame is remote.
+  //
   // If visualRectFlags has the EdgeInclusive bit set, clipping operations will
   // use/ LayoutRect::inclusiveIntersect, and the return value of
   // inclusiveIntersect will be propagated to the return value of this method.
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index fd1500b..c78ee35 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -60,6 +60,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/text/bidi_resolver.h"
+#include "third_party/blink/renderer/platform/text/capitalize.h"
 #include "third_party/blink/renderer/platform/text/character.h"
 #include "third_party/blink/renderer/platform/text/hyphenation.h"
 #include "third_party/blink/renderer/platform/text/text_break_iterator.h"
@@ -112,51 +113,6 @@
   int last_typed_character_offset_;
 };
 
-static void MakeCapitalized(String* string, UChar previous) {
-  if (string->IsNull())
-    return;
-
-  unsigned length = string->length();
-  const StringImpl& input = *string->Impl();
-
-  CHECK_LT(length, std::numeric_limits<unsigned>::max());
-  StringBuffer<UChar> string_with_previous(length + 1);
-  string_with_previous[0] =
-      previous == kNoBreakSpaceCharacter ? kSpaceCharacter : previous;
-  for (unsigned i = 1; i < length + 1; i++) {
-    // Replace &nbsp with a real space since ICU no longer treats &nbsp as a
-    // word separator.
-    if (input[i - 1] == kNoBreakSpaceCharacter)
-      string_with_previous[i] = kSpaceCharacter;
-    else
-      string_with_previous[i] = input[i - 1];
-  }
-
-  TextBreakIterator* boundary =
-      WordBreakIterator(string_with_previous.Characters(), length + 1);
-  if (!boundary)
-    return;
-
-  StringBuilder result;
-  result.ReserveCapacity(length);
-
-  int32_t end_of_word;
-  int32_t start_of_word = boundary->first();
-  for (end_of_word = boundary->next(); end_of_word != kTextBreakDone;
-       start_of_word = end_of_word, end_of_word = boundary->next()) {
-    if (start_of_word) {  // Ignore first char of previous string
-      result.Append(
-          input[start_of_word - 1] == kNoBreakSpaceCharacter
-              ? kNoBreakSpaceCharacter
-              : WTF::Unicode::ToTitleCase(string_with_previous[start_of_word]));
-    }
-    for (int i = start_of_word + 1; i < end_of_word; i++)
-      result.Append(input[i - 1]);
-  }
-
-  *string = result.ToString();
-}
-
 LayoutText::LayoutText(Node* node, scoped_refptr<StringImpl> str)
     : LayoutObject(node),
       has_tab_(false),
@@ -1717,7 +1673,7 @@
     case ETextTransform::kNone:
       break;
     case ETextTransform::kCapitalize:
-      MakeCapitalized(&text, previous_character);
+      text = Capitalize(text, previous_character);
       break;
     case ETextTransform::kUppercase:
       text = text.UpperUnicode(style->Locale());
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 6307baeb..a4fa84b2 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -440,6 +440,13 @@
     transform_state.Move(OffsetForFixedPosition());
 }
 
+bool LayoutView::ShouldClipOverflow() const {
+  bool is_main_frame = GetFrameView()->GetFrame().IsMainFrame();
+  if (is_main_frame && !GetDocument().GetSettings()->GetMainFrameClipsContent())
+    return false;
+  return LayoutBox::ShouldClipOverflow();
+}
+
 void LayoutView::ComputeSelfHitTestRects(Vector<LayoutRect>& rects,
                                          const LayoutPoint&) const {
   // Record the entire size of the contents of the frame. Note that we don't
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index df11eaf..a60ea33 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -259,6 +259,7 @@
   void MapAncestorToLocal(const LayoutBoxModelObject*,
                           TransformState&,
                           MapCoordinatesFlags) const override;
+  bool ShouldClipOverflow() const final;
   void ComputeSelfHitTestRects(Vector<LayoutRect>&,
                                const LayoutPoint& layer_offset) const override;
 
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.h b/third_party/blink/renderer/core/layout/line/inline_box.h
index ddc8753..ae29ffb 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_box.h
@@ -202,9 +202,6 @@
   InlineBox* PrevLeafChild() const;
 
   // Helper functions for editing and hit-testing code.
-  // FIXME: These two functions should be moved to RenderedPosition once the
-  // code to convert between Position and inline box, offset pair is moved to
-  // RenderedPosition.
   InlineBox* NextLeafChildIgnoringLineBreak() const;
   InlineBox* PrevLeafChildIgnoringLineBreak() const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
index f3111c7..2abfb2f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
@@ -6,6 +6,7 @@
 #define NGInlineItem_h
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
@@ -172,6 +173,25 @@
   DCHECK_LE(offset, end_offset_);
 }
 
+// Represents a text content with a list of NGInlineItem. A node may have an
+// additional NGInlineItemsData for ::first-line pseudo element.
+struct CORE_EXPORT NGInlineItemsData {
+  // Text content for all inline items represented by a single NGInlineNode.
+  // Encoded either as UTF-16 or latin-1 depending on the content.
+  String text_content;
+  Vector<NGInlineItem> items;
+
+  // The DOM to text content offset mapping of this inline node.
+  std::unique_ptr<NGOffsetMapping> offset_mapping;
+
+  void AssertOffset(unsigned index, unsigned offset) const {
+    items[index].AssertOffset(offset);
+  }
+  void AssertEndOffset(unsigned index, unsigned offset) const {
+    items[index].AssertEndOffset(offset);
+  }
+};
+
 }  // namespace blink
 
 #endif  // NGInlineItem_h
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
index 74e3e3c..43cdd7b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
@@ -19,14 +19,14 @@
     : item(item), item_index(index), start_offset(start), end_offset(end) {}
 
 void NGLineInfo::SetLineStyle(const NGInlineNode& node,
+                              const NGInlineItemsData& items_data,
                               const NGConstraintSpace& constraint_space,
                               bool is_first_line,
+                              bool use_first_line_style,
                               bool is_after_forced_break) {
-  LayoutObject* layout_object = node.GetLayoutObject();
-  use_first_line_style_ =
-      is_first_line &&
-      layout_object->GetDocument().GetStyleEngine().UsesFirstLineRules();
-  line_style_ = layout_object->Style(use_first_line_style_);
+  use_first_line_style_ = use_first_line_style;
+  items_data_ = &items_data;
+  line_style_ = node.GetLayoutObject()->Style(use_first_line_style_);
 
   if (line_style_->ShouldUseTextIndent(is_first_line, is_after_forced_break)) {
     // 'text-indent' applies to block container, and percentage is of its
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
index 401e52a..514ae110 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
@@ -19,6 +19,8 @@
 class NGInlineItem;
 class NGInlineNode;
 
+struct NGInlineItemsData;
+
 // The result of measuring NGInlineItem.
 //
 // This is a transient context object only while building line boxes.
@@ -106,14 +108,21 @@
   NGLineInfo() = default;
   explicit NGLineInfo(size_t capacity) : results_(capacity) {}
 
+  const NGInlineItemsData& ItemsData() const {
+    DCHECK(items_data_);
+    return *items_data_;
+  }
+
   // The style to use for the line.
   const ComputedStyle& LineStyle() const {
     DCHECK(line_style_);
     return *line_style_;
   }
   void SetLineStyle(const NGInlineNode&,
+                    const NGInlineItemsData&,
                     const NGConstraintSpace&,
-                    bool is_first_line,
+                    bool is_first_formatted_line,
+                    bool use_first_line_style,
                     bool is_after_forced_break);
 
   // Use ::first-line style if true.
@@ -158,6 +167,7 @@
   void SetLineEndFragment(scoped_refptr<NGPhysicalTextFragment>);
 
  private:
+  const NGInlineItemsData* items_data_ = nullptr;
   const ComputedStyle* line_style_ = nullptr;
   NGInlineItemResults results_;
   scoped_refptr<NGPhysicalTextFragment> line_end_fragment_;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index a64b7fa..f2c0b8c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -127,7 +127,7 @@
   DCHECK(break_token->UseFirstLineStyle());
 
   // Compute which tags are not closed at the beginning of this line.
-  const Vector<NGInlineItem>& items = Node().Items();
+  const Vector<NGInlineItem>& items = line_info.ItemsData().items;
   Vector<const NGInlineItem*, 16> open_items;
   for (unsigned i = 0; i < break_token->ItemIndex(); i++) {
     const NGInlineItem& item = items[i];
@@ -191,12 +191,13 @@
                                  baseline_type_);
       }
 
-      text_builder.SetItem(NGPhysicalTextFragment::kNormalText, &item_result,
+      text_builder.SetItem(NGPhysicalTextFragment::kNormalText,
+                           line_info->ItemsData(), &item_result,
                            box->text_height);
       line_box_.AddChild(text_builder.ToTextFragment(), box->text_top,
                          item_result.inline_size, item.BidiLevel());
     } else if (item.Type() == NGInlineItem::kControl) {
-      PlaceControlItem(item, &item_result, box);
+      PlaceControlItem(item, *line_info, &item_result, box);
     } else if (item.Type() == NGInlineItem::kOpenTag) {
       box = HandleOpenTag(item, item_result);
     } else if (item.Type() == NGInlineItem::kCloseTag) {
@@ -301,12 +302,13 @@
 }
 
 void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item,
+                                               const NGLineInfo& line_info,
                                                NGInlineItemResult* item_result,
                                                NGInlineBoxState* box) {
   DCHECK_EQ(item.Type(), NGInlineItem::kControl);
   DCHECK_EQ(item.Length(), 1u);
   DCHECK(!item.TextShapeResult());
-  UChar character = Node().Text()[item.StartOffset()];
+  UChar character = line_info.ItemsData().text_content[item.StartOffset()];
   NGPhysicalTextFragment::NGTextType type;
   switch (character) {
     case kNewlineCharacter:
@@ -334,7 +336,8 @@
 
   NGTextFragmentBuilder text_builder(Node(),
                                      ConstraintSpace().GetWritingMode());
-  text_builder.SetItem(type, item_result, box->text_height);
+  text_builder.SetItem(type, line_info.ItemsData(), item_result,
+                       box->text_height);
   line_box_.AddChild(text_builder.ToTextFragment(), box->text_top,
                      item_result->inline_size, item.BidiLevel());
 }
@@ -463,7 +466,9 @@
 
   // Construct the line text to compute spacing for.
   String line_text =
-      Node().Text(line_info->StartOffset(), align.end_offset).ToString();
+      StringView(line_info->ItemsData().text_content, line_info->StartOffset(),
+                 align.end_offset - line_info->StartOffset())
+          .ToString();
 
   // Append a hyphen if the last word is hyphenated. The hyphen is in
   // |ShapeResult|, but not in text. |ShapeResultSpacing| needs the text that
@@ -575,7 +580,7 @@
   // If we are an empty inline, we don't have to run the full algorithm, we can
   // return now as we should have positioned all of our floats.
   if (is_empty_inline) {
-    DCHECK_EQ(handled_item_index, Node().Items().size());
+    DCHECK_EQ(handled_item_index, Node().ItemsData(false).items.size());
 
     container_builder_.SwapPositionedFloats(&positioned_floats_);
     container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut());
@@ -685,7 +690,7 @@
 // TODO(ikilpatrick): Do we need to always add the OOFs here?
 unsigned NGInlineLayoutAlgorithm::PositionLeadingItems(
     NGExclusionSpace* exclusion_space) {
-  const Vector<NGInlineItem>& items = Node().Items();
+  const Vector<NGInlineItem>& items = Node().ItemsData(false).items;
   bool is_empty_inline = Node().IsEmptyInline();
   LayoutUnit bfc_line_offset = ConstraintSpace().BfcOffset().line_offset;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
index b0c9a2e..b714128 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
@@ -59,6 +59,7 @@
   void BidiReorder();
 
   void PlaceControlItem(const NGInlineItem&,
+                        const NGLineInfo&,
                         NGInlineItemResult*,
                         NGInlineBoxState*);
   void PlaceGeneratedContent(scoped_refptr<NGPhysicalFragment>,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 29dfb0a..51831f7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -183,13 +183,29 @@
   return GetDocument().InLineHeightQuirksMode();
 }
 
+bool NGInlineNode::CanContainFirstFormattedLine() const {
+  // TODO(kojii): In LayoutNG, leading OOF creates an anonymous block box,
+  // and that |LayoutBlockFlow::CanContainFirstFormattedLine()| does not work.
+  // crbug.com/734554
+  LayoutObject* layout_object = GetLayoutBlockFlow();
+  if (!layout_object->IsAnonymousBlock())
+    return true;
+  for (;;) {
+    layout_object = layout_object->PreviousSibling();
+    if (!layout_object)
+      return true;
+    if (!layout_object->IsFloatingOrOutOfFlowPositioned())
+      return false;
+  }
+}
+
 NGInlineNodeData* NGInlineNode::MutableData() {
   return ToLayoutBlockFlow(box_)->GetNGInlineNodeData();
 }
 
 bool NGInlineNode::IsPrepareLayoutFinished() const {
   const NGInlineNodeData* data = ToLayoutBlockFlow(box_)->GetNGInlineNodeData();
-  return data && !data->text_content_.IsNull();
+  return data && !data->text_content.IsNull();
 }
 
 const NGInlineNodeData& NGInlineNode::Data() const {
@@ -198,13 +214,6 @@
   return *ToLayoutBlockFlow(box_)->GetNGInlineNodeData();
 }
 
-const Vector<NGInlineItem>& NGInlineNode::Items(bool is_first_line) const {
-  const NGInlineNodeData& data = Data();
-  if (!is_first_line || !data.first_line_items_)
-    return data.items_;
-  return *data.first_line_items_;
-}
-
 void NGInlineNode::InvalidatePrepareLayoutForTest() {
   GetLayoutBlockFlow()->ResetNGInlineNodeData();
   DCHECK(!IsPrepareLayoutFinished());
@@ -228,6 +237,7 @@
   CollectInlines(data, previous_data.get());
   SegmentText(data);
   ShapeText(data, previous_data.get());
+  ShapeTextForFirstLineIfNeeded(data);
   AssociateItemsWithInlines(data);
   DCHECK_EQ(data, MutableData());
 
@@ -236,10 +246,10 @@
 #if DCHECK_IS_ON()
   // ComputeOffsetMappingIfNeeded() runs some integrity checks as part of
   // creating offset mapping. Run the check, and discard the result.
-  DCHECK(!data->offset_mapping_);
+  DCHECK(!data->offset_mapping);
   ComputeOffsetMappingIfNeeded();
-  DCHECK(data->offset_mapping_);
-  data->offset_mapping_.reset();
+  DCHECK(data->offset_mapping);
+  data->offset_mapping.reset();
 #endif
 }
 
@@ -252,7 +262,7 @@
   DCHECK(!GetLayoutBlockFlow()->GetDocument().NeedsLayoutTreeUpdate());
 
   NGInlineNodeData* data = MutableData();
-  if (!data->offset_mapping_) {
+  if (!data->offset_mapping) {
     // TODO(xiaochengh): ComputeOffsetMappingIfNeeded() discards the
     // NGInlineItems and text content built by |builder|, because they are
     // already there in NGInlineNodeData. For efficiency, we should make
@@ -264,18 +274,18 @@
 
     // The trailing space of the text for offset mapping may be removed. If not,
     // share the string instance.
-    if (text == data->text_content_)
-      text = data->text_content_;
+    if (text == data->text_content)
+      text = data->text_content;
 
     // TODO(xiaochengh): This doesn't compute offset mapping correctly when
     // text-transform CSS property changes text length.
     NGOffsetMappingBuilder& mapping_builder = builder.GetOffsetMappingBuilder();
     mapping_builder.SetDestinationString(text);
-    data->offset_mapping_ =
+    data->offset_mapping =
         std::make_unique<NGOffsetMapping>(mapping_builder.Build());
   }
 
-  return data->offset_mapping_.get();
+  return data->offset_mapping.get();
 }
 
 // Depth-first-scan of all LayoutInline and LayoutText nodes that make up this
@@ -284,23 +294,23 @@
 // string to allow bidi resolution and shaping of the entire block.
 void NGInlineNode::CollectInlines(NGInlineNodeData* data,
                                   NGInlineNodeData* previous_data) {
-  DCHECK(data->text_content_.IsNull());
-  DCHECK(data->items_.IsEmpty());
+  DCHECK(data->text_content.IsNull());
+  DCHECK(data->items.IsEmpty());
   LayoutBlockFlow* block = GetLayoutBlockFlow();
   block->WillCollectInlines();
 
   String* previous_text =
-      previous_data ? &previous_data->text_content_ : nullptr;
-  NGInlineItemsBuilder builder(&data->items_);
+      previous_data ? &previous_data->text_content : nullptr;
+  NGInlineItemsBuilder builder(&data->items);
   CollectInlinesInternal(block, &builder, previous_text);
-  data->text_content_ = builder.ToString();
+  data->text_content = builder.ToString();
 
   // Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this
   // point the string may or may not contain RTL characters.
   // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it
   // doesn't contain any RTL characters.
   data->is_bidi_enabled_ =
-      !data->text_content_.Is8Bit() || builder.HasBidiControls();
+      !data->text_content.Is8Bit() || builder.HasBidiControls();
   data->is_empty_inline_ = builder.IsEmptyInline();
 }
 
@@ -311,8 +321,8 @@
   }
 
   NGBidiParagraph bidi;
-  data->text_content_.Ensure16Bit();
-  if (!bidi.SetParagraph(data->text_content_, Style())) {
+  data->text_content.Ensure16Bit();
+  if (!bidi.SetParagraph(data->text_content, Style())) {
     // On failure, give up bidi resolving and reordering.
     data->is_bidi_enabled_ = false;
     data->SetBaseDirection(TextDirection::kLtr);
@@ -327,9 +337,9 @@
     return;
   }
 
-  Vector<NGInlineItem>& items = data->items_;
+  Vector<NGInlineItem>& items = data->items;
   unsigned item_index = 0;
-  for (unsigned start = 0; start < data->text_content_.length();) {
+  for (unsigned start = 0; start < data->text_content.length();) {
     UBiDiLevel level;
     unsigned end = bidi.GetLogicalRun(start, &level);
     DCHECK_EQ(items[item_index].start_offset_, start);
@@ -347,14 +357,12 @@
 #endif
 }
 
-void NGInlineNode::ShapeText(NGInlineNodeData* data,
-                             NGInlineNodeData* previous_data) {
+void NGInlineNode::ShapeText(NGInlineItemsData* data,
+                             NGInlineItemsData* previous_data) {
   // TODO(eae): Add support for shaping latin-1 text?
-  data->text_content_.Ensure16Bit();
-  ShapeText(data->text_content_, &data->items_,
-            previous_data ? &previous_data->text_content_ : nullptr);
-
-  ShapeTextForFirstLineIfNeeded(data);
+  data->text_content.Ensure16Bit();
+  ShapeText(data->text_content, &data->items,
+            previous_data ? &previous_data->text_content : nullptr);
 }
 
 void NGInlineNode::ShapeText(const String& text_content,
@@ -485,9 +493,13 @@
   if (block_style == first_line_style)
     return;
 
-  auto first_line_items = std::make_unique<Vector<NGInlineItem>>();
-  first_line_items->AppendVector(data->items_);
-  for (auto& item : *first_line_items) {
+  auto first_line_items = std::make_unique<NGInlineItemsData>();
+  // TODO(kojii): Support 'text-transform' to change the text_content.
+  // When the text changes, offsets in items are also changed. This might need
+  // to change this function a bit drastically.
+  first_line_items->text_content = data->text_content;
+  first_line_items->items.AppendVector(data->items);
+  for (auto& item : first_line_items->items) {
     if (item.style_) {
       DCHECK(item.layout_object_);
       item.style_ = item.layout_object_->FirstLineStyle();
@@ -498,16 +510,15 @@
   // Re-shape if the font is different.
   const Font& font = block_style->GetFont();
   const Font& first_line_font = first_line_style->GetFont();
-  if (&font != &first_line_font && font != first_line_font) {
-    ShapeText(data->text_content_, first_line_items.get(), nullptr);
-  }
+  if (&font != &first_line_font && font != first_line_font)
+    ShapeText(first_line_items.get());
 
   data->first_line_items_ = std::move(first_line_items);
 }
 
 void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) {
   LayoutObject* last_object = nullptr;
-  for (auto& item : data->items_) {
+  for (auto& item : data->items) {
     LayoutObject* object = item.GetLayoutObject();
     if (object && object->IsLayoutNGText()) {
       LayoutNGText* layout_text = ToLayoutNGText(object);
@@ -656,7 +667,7 @@
 
 void NGInlineNode::CheckConsistency() const {
 #if DCHECK_IS_ON()
-  const Vector<NGInlineItem>& items = Data().items_;
+  const Vector<NGInlineItem>& items = Data().items;
   for (const NGInlineItem& item : items) {
     DCHECK(!item.GetLayoutObject() || !item.Style() ||
            item.Style() == item.GetLayoutObject()->Style());
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
index 81019b5..203ddf4 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
@@ -56,14 +56,10 @@
   // Instruct to re-compute |PrepareLayout| on the next layout.
   void InvalidatePrepareLayoutForTest();
 
-  const String& Text() const { return Data().text_content_; }
-  StringView Text(unsigned start_offset, unsigned end_offset) const {
-    return StringView(Data().text_content_, start_offset,
-                      end_offset - start_offset);
+  const NGInlineItemsData& ItemsData(bool is_first_line) const {
+    return Data().ItemsData(is_first_line);
   }
 
-  const Vector<NGInlineItem>& Items(bool is_first_line = false) const;
-
   // Returns the DOM to text content offset mapping of this block. If it is not
   // computed before, compute and store it in NGInlineNodeData.
   // This funciton must be called with clean layout.
@@ -74,8 +70,10 @@
 
   bool IsEmptyInline() { return EnsureData().is_empty_inline_; }
 
-  void AssertOffset(unsigned index, unsigned offset) const;
-  void AssertEndOffset(unsigned index, unsigned offset) const;
+  // @return if this node can contain the "first formatted line".
+  // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line
+  bool CanContainFirstFormattedLine() const;
+
   void CheckConsistency() const;
 
   String ToString() const;
@@ -90,7 +88,8 @@
   void CollectInlines(NGInlineNodeData*,
                       NGInlineNodeData* previous_data = nullptr);
   void SegmentText(NGInlineNodeData*);
-  void ShapeText(NGInlineNodeData*, NGInlineNodeData* previous_data = nullptr);
+  void ShapeText(NGInlineItemsData*,
+                 NGInlineItemsData* previous_data = nullptr);
   void ShapeText(const String& text,
                  Vector<NGInlineItem>*,
                  const String* previous_text);
@@ -105,15 +104,6 @@
   friend class NGInlineNodeLegacy;
 };
 
-inline void NGInlineNode::AssertOffset(unsigned index, unsigned offset) const {
-  Data().items_[index].AssertOffset(offset);
-}
-
-inline void NGInlineNode::AssertEndOffset(unsigned index,
-                                          unsigned offset) const {
-  Data().items_[index].AssertEndOffset(offset);
-}
-
 DEFINE_TYPE_CASTS(NGInlineNode,
                   NGLayoutInputNode,
                   node,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
index 2d8efffd..f19defd 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
@@ -11,16 +11,19 @@
 
 namespace blink {
 
-class NGOffsetMapping;
-
 // Data which is required for inline nodes.
-struct CORE_EXPORT NGInlineNodeData {
+struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
   // The constructor and destructor can't be implicit or inlined, because they
   // need full definition of NGOffsetMapping.
   NGInlineNodeData();
   ~NGInlineNodeData();
 
  private:
+  const NGInlineItemsData& ItemsData(bool is_first_line) const {
+    return !is_first_line || !first_line_items_
+               ? (const NGInlineItemsData&)*this
+               : *first_line_items_;
+  }
   TextDirection BaseDirection() const {
     return static_cast<TextDirection>(base_direction_);
   }
@@ -33,18 +36,12 @@
   friend class NGInlineNodeForTest;
   friend class NGOffsetMappingTest;
 
-  // Text content for all inline items represented by a single NGInlineNode.
-  // Encoded either as UTF-16 or latin-1 depending on the content.
-  String text_content_;
-  Vector<NGInlineItem> items_;
-
-  // |items_| to use for the first line, when the node has :first-line rules.
-  // Items have different ComputedStyle, and may also have different ShapeResult
-  // if fonts are different.
-  std::unique_ptr<Vector<NGInlineItem>> first_line_items_;
-
-  // The DOM to text content offset mapping of this inline node.
-  std::unique_ptr<NGOffsetMapping> offset_mapping_;
+  // Items to use for the first line, when the node has :first-line rules.
+  //
+  // Items have different ComputedStyle, and may also have different
+  // text_content and ShapeResult if 'text-transform' is applied or fonts are
+  // different.
+  std::unique_ptr<NGInlineItemsData> first_line_items_;
 
   unsigned is_bidi_enabled_ : 1;
   unsigned base_direction_ : 1;  // TextDirection
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
index b209881b..f04e5e7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
@@ -22,29 +22,29 @@
  public:
   using NGInlineNode::NGInlineNode;
 
-  std::string Text() const { return Data().text_content_.Utf8().data(); }
-  Vector<NGInlineItem>& Items() { return MutableData()->items_; }
+  std::string Text() const { return Data().text_content.Utf8().data(); }
+  Vector<NGInlineItem>& Items() { return MutableData()->items; }
   static Vector<NGInlineItem>& Items(NGInlineNodeData& data) {
-    return data.items_;
+    return data.items;
   }
 
   void Append(const String& text,
               const ComputedStyle* style = nullptr,
               LayoutObject* layout_object = nullptr) {
     NGInlineNodeData* data = MutableData();
-    unsigned start = data->text_content_.length();
-    data->text_content_.append(text);
-    data->items_.push_back(NGInlineItem(NGInlineItem::kText, start,
-                                        start + text.length(), style,
-                                        layout_object));
+    unsigned start = data->text_content.length();
+    data->text_content.append(text);
+    data->items.push_back(NGInlineItem(NGInlineItem::kText, start,
+                                       start + text.length(), style,
+                                       layout_object));
     data->is_empty_inline_ = false;
   }
 
   void Append(UChar character) {
     NGInlineNodeData* data = MutableData();
-    data->text_content_.append(character);
-    unsigned end = data->text_content_.length();
-    data->items_.push_back(
+    data->text_content.append(character);
+    unsigned end = data->text_content.length();
+    data->items.push_back(
         NGInlineItem(NGInlineItem::kBidiControl, end - 1, end, nullptr));
     data->is_bidi_enabled_ = true;
     data->is_empty_inline_ = false;
@@ -52,8 +52,8 @@
 
   void ClearText() {
     NGInlineNodeData* data = MutableData();
-    data->text_content_ = String();
-    data->items_.clear();
+    data->text_content = String();
+    data->items.clear();
     data->is_empty_inline_ = true;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 6e576cf..76fa8e0 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -35,6 +35,17 @@
 
 }  // namespace
 
+NGLineBreaker::LineData::LineData(NGInlineNode node,
+                                  const NGInlineBreakToken* break_token) {
+  is_first_formatted_line = (!break_token || (!break_token->ItemIndex() &&
+                                              !break_token->TextOffset())) &&
+                            node.CanContainFirstFormattedLine();
+  use_first_line_style = is_first_formatted_line && node.GetLayoutObject()
+                                                        ->GetDocument()
+                                                        .GetStyleEngine()
+                                                        .UsesFirstLineRules();
+}
+
 NGLineBreaker::NGLineBreaker(
     NGInlineNode node,
     NGLineBreakerMode mode,
@@ -45,16 +56,19 @@
     NGExclusionSpace* exclusion_space,
     unsigned handled_float_index,
     const NGInlineBreakToken* break_token)
-    : node_(node),
+    : line_(node, break_token),
+      node_(node),
+      items_data_(node.ItemsData(line_.use_first_line_style)),
       mode_(mode),
       constraint_space_(space),
       positioned_floats_(positioned_floats),
       unpositioned_floats_(unpositioned_floats),
       container_builder_(container_builder),
       exclusion_space_(exclusion_space),
-      break_iterator_(node.Text()),
-      shaper_(node.Text().Characters16(), node.Text().length()),
-      spacing_(node.Text()),
+      break_iterator_(items_data_.text_content),
+      shaper_(items_data_.text_content.Characters16(),
+              items_data_.text_content.length()),
+      spacing_(items_data_.text_content),
       handled_floats_end_item_index_(handled_float_index),
       base_direction_(node_.BaseDirection()),
       in_line_height_quirks_mode_(node.InLineHeightQuirksMode()) {
@@ -65,7 +79,7 @@
     item_index_ = break_token->ItemIndex();
     offset_ = break_token->TextOffset();
     previous_line_had_forced_break_ = break_token->IsForcedBreak();
-    node.AssertOffset(item_index_, offset_);
+    items_data_.AssertOffset(item_index_, offset_);
     ignore_floats_ = break_token->IgnoreFloats();
   }
 }
@@ -112,8 +126,7 @@
 // whitespace collapsing.
 bool NGLineBreaker::IsTrailing(const NGInlineItem& item,
                                const NGLineInfo& line_info) const {
-  const Vector<NGInlineItem>& items =
-      node_.Items(line_info.UseFirstLineStyle());
+  const Vector<NGInlineItem>& items = line_info.ItemsData().items;
   for (const NGInlineItem* it = &item; it != items.end(); ++it) {
     if (it->EndCollapseType() != NGInlineItem::kOpaqueToCollapsing)
       return false;
@@ -121,42 +134,22 @@
   return true;
 }
 
-// @return if this is the "first formatted line".
-// https://www.w3.org/TR/CSS22/selector.html#first-formatted-line
-bool NGLineBreaker::IsFirstFormattedLine() const {
-  if (item_index_ || offset_)
-    return false;
-
-  // TODO(kojii): In LayoutNG, leading OOF creates an anonymous block box,
-  // and that |CanContainFirstFormattedLine()| does not work.
-  // crbug.com/734554
-  // return node_.GetLayoutBlockFlow()->CanContainFirstFormattedLine();
-  LayoutObject* layout_object = node_.GetLayoutBlockFlow();
-  if (!layout_object->IsAnonymousBlock())
-    return true;
-  for (;;) {
-    layout_object = layout_object->PreviousSibling();
-    if (!layout_object)
-      return true;
-    if (!layout_object->IsFloatingOrOutOfFlowPositioned())
-      return false;
-  }
-}
-
 // Compute the base direction for bidi algorithm for this line.
-void NGLineBreaker::ComputeBaseDirection() {
+void NGLineBreaker::ComputeBaseDirection(const NGLineInfo& line_info) {
   // If 'unicode-bidi' is not 'plaintext', use the base direction of the block.
   if (!previous_line_had_forced_break_ ||
       node_.Style().GetUnicodeBidi() != UnicodeBidi::kPlaintext)
     return;
   // If 'unicode-bidi: plaintext', compute the base direction for each paragraph
   // (separated by forced break.)
-  const String& text = node_.Text();
+  const String& text = line_info.ItemsData().text_content;
   if (text.Is8Bit())
     return;
   size_t end_offset = text.find(kNewlineCharacter, offset_);
-  base_direction_ = NGBidiParagraph::BaseDirectionForString(node_.Text(
-      offset_, end_offset == kNotFound ? text.length() : end_offset));
+  base_direction_ = NGBidiParagraph::BaseDirectionForString(
+      end_offset == kNotFound
+          ? StringView(text, offset_)
+          : StringView(text, offset_, end_offset - offset_));
 }
 
 // Initialize internal states for the next line.
@@ -165,15 +158,16 @@
   NGInlineItemResults* item_results = &line_info->Results();
   item_results->clear();
   line_info->SetStartOffset(offset_);
-  line_info->SetLineStyle(node_, constraint_space_, IsFirstFormattedLine(),
-                          previous_line_had_forced_break_);
+  line_info->SetLineStyle(
+      node_, items_data_, constraint_space_, line_.is_first_formatted_line,
+      line_.use_first_line_style, previous_line_had_forced_break_);
   // Set the initial style of this line from the break token. Example:
   //   <p>...<span>....</span></p>
   // When the line wraps in <span>, the 2nd line needs to start with the style
   // of the <span>.
   override_break_anywhere_ = false;
   SetCurrentStyle(current_style_ ? *current_style_ : line_info->LineStyle());
-  ComputeBaseDirection();
+  ComputeBaseDirection(*line_info);
   line_info->SetBaseDirection(base_direction_);
 
   line_.is_after_forced_break = false;
@@ -209,8 +203,7 @@
 
 void NGLineBreaker::BreakLine(NGLineInfo* line_info) {
   NGInlineItemResults* item_results = &line_info->Results();
-  const Vector<NGInlineItem>& items =
-      node_.Items(line_info->UseFirstLineStyle());
+  const Vector<NGInlineItem>& items = line_info->ItemsData().items;
   LineBreakState state = LineBreakState::kContinue;
   while (state != LineBreakState::kDone) {
     // Check overflow even if |item_index_| is at the end of the block, because
@@ -841,7 +834,7 @@
       // be a break opportunity after the space. The break_iterator cannot
       // compute this because it considers break opportunities are before a run
       // of spaces.
-      const String& text = node_.Text();
+      const String& text = Text();
       if (offset_ < text.length() && IsBreakableSpace(text[offset_])) {
         item_result->can_break_after = true;
         return;
@@ -916,7 +909,7 @@
 #endif
           item_index_ = item_result->item_index;
           offset_ = item_result->end_offset;
-          node_.AssertOffset(item_index_, offset_);
+          items_data_.AssertOffset(item_index_, offset_);
         } else {
           Rewind(line_info, i + 1);
         }
@@ -976,8 +969,7 @@
 // paint invalidations, hit testing, etc.
 LayoutObject* NGLineBreaker::CurrentLayoutObject(
     const NGLineInfo& line_info) const {
-  const Vector<NGInlineItem>& items =
-      node_.Items(line_info.UseFirstLineStyle());
+  const Vector<NGInlineItem>& items = line_info.ItemsData().items;
   DCHECK_LE(item_index_, items.size());
   // Find the next item that has LayoutObject. Some items such as bidi controls
   // do not have LayoutObject.
@@ -1049,7 +1041,7 @@
 scoped_refptr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken(
     const NGLineInfo& line_info,
     std::unique_ptr<const NGInlineLayoutStateStack> state_stack) const {
-  const Vector<NGInlineItem>& items = node_.Items();
+  const Vector<NGInlineItem>& items = Items();
   if (item_index_ >= items.size())
     return NGInlineBreakToken::Create(node_);
   return NGInlineBreakToken::Create(
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index 5f2c6b4..01ff0d8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -65,6 +65,8 @@
   struct LineData {
     STACK_ALLOCATED();
 
+    LineData(NGInlineNode node, const NGInlineBreakToken* break_token);
+
     // The current position from inline_start. Unlike NGInlineLayoutAlgorithm
     // that computes position in visual order, this position in logical order.
     LayoutUnit position;
@@ -75,6 +77,12 @@
     LayoutUnit line_left_bfc_offset;
     LayoutUnit line_right_bfc_offset;
 
+    // True if this line is the "first formatted line".
+    // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line
+    bool is_first_formatted_line;
+
+    bool use_first_line_style;
+
     // We don't create "certain zero-height line boxes".
     // https://drafts.csswg.org/css2/visuren.html#phantom-line-box
     // Such line boxes do not prevent two margins being "adjoining", and thus
@@ -96,7 +104,9 @@
     }
   };
 
-  const String& Text() const { return break_iterator_.GetString(); }
+  const String& Text() const { return items_data_.text_content; }
+  const Vector<NGInlineItem>& Items() const { return items_data_.items; }
+
   NGInlineItemResult* AddItem(const NGInlineItem&,
                               unsigned end_offset,
                               NGInlineItemResults*);
@@ -156,12 +166,13 @@
   void MoveToNextOf(const NGInlineItem&);
   void MoveToNextOf(const NGInlineItemResult&);
 
-  bool IsFirstFormattedLine() const;
-  void ComputeBaseDirection();
+  void ComputeBaseDirection(const NGLineInfo&);
   bool IsTrailing(const NGInlineItem&, const NGLineInfo&) const;
 
   LineData line_;
   NGInlineNode node_;
+  const NGInlineItemsData& items_data_;
+
   NGLineBreakerMode mode_;
   const NGConstraintSpace& constraint_space_;
   Vector<NGPositionedFloat>* positioned_floats_;
@@ -172,12 +183,12 @@
 
   unsigned item_index_ = 0;
   unsigned offset_ = 0;
-  bool previous_line_had_forced_break_ = false;
-  LayoutUnit bfc_line_offset_;
-  LayoutUnit bfc_block_offset_;
   LazyLineBreakIterator break_iterator_;
   HarfBuzzShaper shaper_;
   ShapeResultSpacing<String> spacing_;
+  bool previous_line_had_forced_break_ = false;
+  LayoutUnit bfc_line_offset_;
+  LayoutUnit bfc_block_offset_;
   const Hyphenation* hyphenation_ = nullptr;
 
   // Keep track of handled float items. See HandleFloat().
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
index 0394eadf..34e1835 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
@@ -70,8 +70,11 @@
 
 String ToString(NGInlineItemResults line, NGInlineNode node) {
   StringBuilder builder;
+  const String& text = node.ItemsData(false).text_content;
   for (const auto& item_result : line) {
-    builder.Append(node.Text(item_result.start_offset, item_result.end_offset));
+    builder.Append(
+        StringView(text, item_result.start_offset,
+                   item_result.end_offset - item_result.start_offset));
   }
   return builder.ToString();
 }
@@ -182,7 +185,7 @@
     </style>
     <div id=container><span>123 456</span> 789</div>
   )HTML");
-  const Vector<NGInlineItem>& items = node.Items();
+  const Vector<NGInlineItem>& items = node.ItemsData(false).items;
 
   // While "123 456" can fit in a line, "456" has a right margin that cannot
   // fit. Since "456" and its right margin is not breakable, "456" should be on
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
index 38bb8d4..1036a00 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
@@ -40,7 +40,7 @@
   }
 
   bool IsOffsetMappingStored() const {
-    return layout_block_flow_->GetNGInlineNodeData()->offset_mapping_.get();
+    return layout_block_flow_->GetNGInlineNodeData()->offset_mapping.get();
   }
 
   const LayoutText* GetLayoutTextUnder(const char* parent_id) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc
index 8852bb2d..62c3347 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc
@@ -37,13 +37,14 @@
 
 void NGTextFragmentBuilder::SetItem(
     NGPhysicalTextFragment::NGTextType text_type,
+    const NGInlineItemsData& items_data,
     NGInlineItemResult* item_result,
     LayoutUnit line_height) {
   DCHECK(item_result);
   DCHECK(item_result->item->Style());
 
   text_type_ = text_type;
-  text_ = inline_node_.Text();
+  text_ = items_data.text_content;
   item_index_ = item_result->item_index;
   start_offset_ = item_result->start_offset;
   end_offset_ = item_result->end_offset;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h
index b53ca07..68efb30 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h
@@ -26,6 +26,7 @@
 
   // NOTE: Takes ownership of the shape result within the item result.
   void SetItem(NGPhysicalTextFragment::NGTextType,
+               const NGInlineItemsData&,
                NGInlineItemResult*,
                LayoutUnit line_height);
   void SetText(LayoutObject*,
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index 3ff56c3..919a01fc 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -156,6 +156,14 @@
   // The checks above should be enough to bail if layout is incomplete, but
   // let's verify:
   DCHECK(IsBlockLayoutComplete(*cached_constraint_space_, *cached_result_));
+  // If we used to contain abspos items, we can't reuse the fragment, because
+  // we can't be sure that the list of items hasn't changed (as we bubble them
+  // up during layout). In the case of newly-added abspos items to this
+  // containing block, we will handle those by the NeedsLayout check above for
+  // now.
+  // TODO(layout-ng): Come up with a better solution for this
+  if (cached_result_->OutOfFlowPositionedDescendants().size())
+    return nullptr;
   return cached_result_->CloneWithoutOffset();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
index 4990926..6981bf115 100644
--- a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
@@ -55,6 +55,10 @@
     const NGPhysicalFragment& content,
     NGLogicalOffset* content_offset,
     NGFragmentBuilder* container_builder) const {
+  // Baselines from two different writing-mode cannot be aligned.
+  if (UNLIKELY(space.GetWritingMode() != content.Style().GetWritingMode()))
+    return false;
+
   // Compute the baseline of the child content.
   FontBaseline baseline_type = IsHorizontalWritingMode(space.GetWritingMode())
                                    ? kAlphabeticBaseline
@@ -66,7 +70,7 @@
     NGBoxFragment content_fragment(space.GetWritingMode(),
                                    ToNGPhysicalBoxFragment(content));
     content_metrics = content_fragment.BaselineMetricsWithoutSynthesize(
-        {NGBaselineAlgorithmType::kFirstLine, baseline_type}, space);
+        {NGBaselineAlgorithmType::kFirstLine, baseline_type});
 
     // If this child content does not have any line boxes, the list marker
     // should be aligned to the first line box of next child.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index c2ec41d..7aba5c9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -669,9 +669,6 @@
 
     // We reset the block offset here as it may have been affected by clearance.
     child_bfc_offset_estimate = ContainerBfcOffset().block_offset;
-
-    // Position any pending floats since we've just updated our BFC offset.
-    PositionPendingFloats(child_bfc_offset_estimate);
   }
 
   // If the child has a non-zero block-start margin, our initial estimate will
@@ -930,12 +927,13 @@
   // to our parent.
   if (layout_result->Status() == NGLayoutResult::kBfcOffsetResolved &&
       !container_builder_.BfcOffset()) {
+    // There's no need to do anything apart from resolving the BFC offset here,
+    // so make sure that it aborts before trying to position floats or anything
+    // like that, which would just be waste of time. This is simply propagating
+    // an abort up to a node which is able to restart the layout (a node that
+    // has resolved its BFC offset).
+    abort_when_bfc_offset_updated_ = true;
     ResolveBfcOffset(previous_inflow_position, child_bfc_offset->block_offset);
-
-    // NOTE: Unlike other aborts, we don't try check if we *should* abort with
-    // NeedsAbortOnBfcOffsetChange(), this is simply propagating an abort up to
-    // a node which is able to restart the layout (a node that has resolved its
-    // BFC offset).
     return false;
   }
 
@@ -1143,6 +1141,10 @@
 
   bool is_empty_block = IsEmptyBlock(child, layout_result);
   if (is_empty_block) {
+    // The default behaviour for empty blocks is they just pass through the
+    // previous inflow position.
+    child_end_bfc_block_offset = previous_inflow_position.bfc_block_offset;
+
     if (empty_block_affected_by_clearance) {
       // If an empty block was affected by clearance (that is it got pushed
       // down past a float), we need to do something slightly bizarre.
@@ -1154,18 +1156,46 @@
       // Another way of thinking about this is that when you *add* back the
       // margin strut, you end up with the same position as you started with.
       //
-      // This behaviour isn't known to be in any CSS specification.
-      child_end_bfc_block_offset = child_bfc_offset.value().block_offset -
-                                   layout_result.EndMarginStrut().Sum();
-      logical_block_offset =
-          logical_offset.block_offset - layout_result.EndMarginStrut().Sum();
-    } else {
-      // The default behaviour for empty blocks is they just pass through the
-      // previous inflow position.
-      child_end_bfc_block_offset = previous_inflow_position.bfc_block_offset;
-      logical_block_offset = previous_inflow_position.logical_block_offset;
+      // This is essentially what the spec refers to as clearance [1], and,
+      // while we normally don't have to calculate it directly, in the case of
+      // an empty cleared child like here, we actually have to.
+      //
+      // We have to calculate clearance for empty cleared children, because we
+      // need the margin that's between the clearance and this block to collapse
+      // correctly with subsequent content. This is something that needs to take
+      // place after the margin strut preceding and following the clearance have
+      // been separated. Clearance may be positive, negative or zero, depending
+      // on what it takes to (hypothetically) place this child just below the
+      // last relevant float. Since the margins before and after the clearance
+      // have been separated, we may have to pull the child back, and that's an
+      // example of negative clearance.
+      //
+      // (In the other case, when a cleared child is non-empty (i.e. when we
+      // don't end up here), we don't need to explicitly calculate clearance,
+      // because then we just place its border edge where it should be and we're
+      // done with it.)
+      //
+      // [1] https://www.w3.org/TR/CSS22/visuren.html#flow-control
+
+      // First move past the margin that is to precede the clearance. It will
+      // not participate in any subsequent margin collapsing.
+      LayoutUnit margin_before_clearance =
+          previous_inflow_position.margin_strut.Sum();
+      child_end_bfc_block_offset += margin_before_clearance;
+
+      // Calculate and apply actual clearance.
+      LayoutUnit clearance = child_bfc_offset.value().block_offset -
+                             layout_result.EndMarginStrut().Sum() -
+                             previous_inflow_position.NextBorderEdge();
+      child_end_bfc_block_offset += clearance;
     }
 
+    // The logical block offset needs to go through exactly the same change as
+    // the BFC block offset here.
+    logical_block_offset = previous_inflow_position.logical_block_offset +
+                           child_end_bfc_block_offset -
+                           previous_inflow_position.bfc_block_offset;
+
     if (!container_builder_.BfcOffset()) {
       DCHECK_EQ(child_end_bfc_block_offset,
                 ConstraintSpace().BfcOffset().block_offset);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 1829ab8..5b69e27 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -594,44 +594,53 @@
   if (requests.IsEmpty())
     return;
 
+  if (UNLIKELY(constraint_space.GetWritingMode() != Style().GetWritingMode()))
+    return;
+
   for (const auto& request : requests) {
     switch (request.algorithm_type) {
-      case NGBaselineAlgorithmType::kAtomicInline:
-        AddAtomicInlineBaselineFromOldLayout(
-            request, constraint_space.UseFirstLineStyle(), builder);
+      case NGBaselineAlgorithmType::kAtomicInline: {
+        LayoutUnit position =
+            AtomicInlineBaselineFromOldLayout(request, constraint_space);
+        if (position != -1)
+          builder->AddBaseline(request, position);
         break;
+      }
       case NGBaselineAlgorithmType::kFirstLine: {
         LayoutUnit position = box_->FirstLineBoxBaseline();
-        if (position != -1) {
+        if (position != -1)
           builder->AddBaseline(request, position);
-        }
         break;
       }
     }
   }
 }
 
-void NGBlockNode::AddAtomicInlineBaselineFromOldLayout(
+LayoutUnit NGBlockNode::AtomicInlineBaselineFromOldLayout(
     const NGBaselineRequest& request,
-    bool is_first_line,
-    NGFragmentBuilder* builder) {
-  // Block-level boxes do not have atomic inline baseline.
-  // This includes form controls when 'display:block' is applied.
-  if (box_->IsLayoutBlock() && !box_->IsInline())
-    return;
+    const NGConstraintSpace& constraint_space) {
+  LineDirectionMode line_direction = box_->IsHorizontalWritingMode()
+                                         ? LineDirectionMode::kHorizontalLine
+                                         : LineDirectionMode::kVerticalLine;
 
-  LineDirectionMode line_direction =
-      IsHorizontalWritingMode(builder->GetWritingMode())
-          ? LineDirectionMode::kHorizontalLine
-          : LineDirectionMode::kVerticalLine;
-  LayoutUnit position = LayoutUnit(box_->BaselinePosition(
-      request.baseline_type, is_first_line, line_direction));
+  // If this is an inline box, use |BaselinePosition()|. Some LayoutObject
+  // classes override it assuming inline layout calls |BaselinePosition()|.
+  if (box_->IsInline()) {
+    LayoutUnit position = LayoutUnit(box_->BaselinePosition(
+        request.baseline_type, constraint_space.UseFirstLineStyle(),
+        line_direction, kPositionOnContainingLine));
 
-  // BaselinePosition() uses margin edge for atomic inlines.
-  if (box_->IsAtomicInlineLevel())
-    position -= box_->MarginOver();
+    // BaselinePosition() uses margin edge for atomic inlines. Subtract
+    // margin-over so that the position is relative to the border box.
+    if (box_->IsAtomicInlineLevel())
+      position -= box_->MarginOver();
 
-  builder->AddBaseline(request, position);
+    return position;
+  }
+
+  // If this is a block box, use |InlineBlockBaseline()|. When an inline block
+  // has block children, their inline block baselines need to be propagated.
+  return box_->InlineBlockBaseline(line_direction);
 }
 
 void NGBlockNode::UseOldOutOfFlowPositioning() {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index 05f50c3..25c27d7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -89,9 +89,8 @@
       const NGPhysicalOffset& additional_offset = NGPhysicalOffset());
 
   void CopyBaselinesFromOldLayout(const NGConstraintSpace&, NGFragmentBuilder*);
-  void AddAtomicInlineBaselineFromOldLayout(const NGBaselineRequest&,
-                                            bool,
-                                            NGFragmentBuilder*);
+  LayoutUnit AtomicInlineBaselineFromOldLayout(const NGBaselineRequest&,
+                                               const NGConstraintSpace&);
 };
 
 DEFINE_TYPE_CASTS(NGBlockNode,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
index 3ee4744..c43e846 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
@@ -14,29 +14,36 @@
 namespace blink {
 
 NGLineHeightMetrics NGBoxFragment::BaselineMetricsWithoutSynthesize(
-    const NGBaselineRequest& request,
-    const NGConstraintSpace& constraint_space) const {
+    const NGBaselineRequest& request) const {
+  // For "leaf" theme objects, let the theme decide what the baseline position
+  // is. The theme baseline wins over the propagated baselines.
   const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_);
-  bool is_parallel_writing_mode =
-      IsParallelWritingMode(constraint_space.GetWritingMode(),
-                            physical_fragment.Style().GetWritingMode());
-  if (is_parallel_writing_mode) {
-    // Find the baseline from the computed results.
-    if (const NGBaseline* baseline = physical_fragment.Baseline(request)) {
-      LayoutUnit ascent = baseline->offset;
-      LayoutUnit descent = BlockSize() - ascent;
+  DCHECK(physical_fragment_.GetLayoutObject());
+  const LayoutBox& layout_box =
+      ToLayoutBox(*physical_fragment_.GetLayoutObject());
+  const ComputedStyle& style = physical_fragment.Style();
+  if (style.HasAppearance() &&
+      !LayoutTheme::GetTheme().IsControlContainer(style.Appearance())) {
+    return NGLineHeightMetrics(
+        BlockSize() + layout_box.MarginOver() +
+            LayoutTheme::GetTheme().BaselinePositionAdjustment(style),
+        layout_box.MarginUnder());
+  }
 
-      // For replaced elements, inline-block elements, and inline-table
-      // elements, the height is the height of their margin box.
-      // https://drafts.csswg.org/css2/visudet.html#line-height
-      LayoutBox* layout_box = ToLayoutBox(physical_fragment_.GetLayoutObject());
-      if (layout_box->IsAtomicInlineLevel()) {
-        ascent += layout_box->MarginOver();
-        descent += layout_box->MarginUnder();
-      }
+  // Check if we have a propagated baseline.
+  if (const NGBaseline* baseline = physical_fragment.Baseline(request)) {
+    LayoutUnit ascent = baseline->offset;
+    LayoutUnit descent = BlockSize() - ascent;
 
-      return NGLineHeightMetrics(ascent, descent);
+    // For replaced elements, inline-block elements, and inline-table
+    // elements, the height is the height of their margin box.
+    // https://drafts.csswg.org/css2/visudet.html#line-height
+    if (layout_box.IsAtomicInlineLevel()) {
+      ascent += layout_box.MarginOver();
+      descent += layout_box.MarginUnder();
     }
+
+    return NGLineHeightMetrics(ascent, descent);
   }
 
   return NGLineHeightMetrics();
@@ -45,36 +52,31 @@
 NGLineHeightMetrics NGBoxFragment::BaselineMetrics(
     const NGBaselineRequest& request,
     const NGConstraintSpace& constraint_space) const {
-  NGLineHeightMetrics metrics =
-      BaselineMetricsWithoutSynthesize(request, constraint_space);
-  if (!metrics.IsEmpty())
-    return metrics;
+  // Try to compute the baseline if the writing-modes are the same.
+  if (constraint_space.GetWritingMode() == GetWritingMode()) {
+    NGLineHeightMetrics metrics = BaselineMetricsWithoutSynthesize(request);
+    if (!metrics.IsEmpty())
+      return metrics;
+  }
 
   // The baseline type was not found. This is either this box should synthesize
   // box-baseline without propagating from children, or caller forgot to add
   // baseline requests to constraint space when it called Layout().
   LayoutUnit block_size = BlockSize();
 
-  const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_);
-  const ComputedStyle& style = physical_fragment.Style();
-  LayoutBox* layout_box = ToLayoutBox(physical_fragment_.GetLayoutObject());
-  if (style.HasAppearance() &&
-      !LayoutTheme::GetTheme().IsControlContainer(style.Appearance())) {
-    return NGLineHeightMetrics(
-        block_size + layout_box->MarginOver() +
-            LayoutTheme::GetTheme().BaselinePositionAdjustment(style),
-        layout_box->MarginUnder());
-  }
-
   // If atomic inline, use the margin box. See above.
-  if (layout_box->IsAtomicInlineLevel()) {
+  const auto& physical_fragment = ToNGPhysicalBoxFragment(physical_fragment_);
+  DCHECK(physical_fragment_.GetLayoutObject());
+  const LayoutBox& layout_box =
+      ToLayoutBox(*physical_fragment_.GetLayoutObject());
+  if (layout_box.IsAtomicInlineLevel()) {
     bool is_parallel_writing_mode =
         IsParallelWritingMode(constraint_space.GetWritingMode(),
                               physical_fragment.Style().GetWritingMode());
     if (is_parallel_writing_mode)
-      block_size += layout_box->MarginLogicalHeight();
+      block_size += layout_box.MarginLogicalHeight();
     else
-      block_size += layout_box->MarginLogicalWidth();
+      block_size += layout_box.MarginLogicalWidth();
   }
 
   if (request.baseline_type == kAlphabeticBaseline)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
index 3f3b3dbe..bd7e6453 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
@@ -31,8 +31,7 @@
   // not have any baselines, while the other version synthesize the baseline
   // from the box.
   NGLineHeightMetrics BaselineMetricsWithoutSynthesize(
-      const NGBaselineRequest&,
-      const NGConstraintSpace&) const;
+      const NGBaselineRequest&) const;
   NGLineHeightMetrics BaselineMetrics(const NGBaselineRequest&,
                                       const NGConstraintSpace&) const;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
index 187a194..56477f84 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -15,19 +15,6 @@
 
 namespace blink {
 
-namespace {
-
-bool ShouldComputeBaseline(const LayoutBox& box) {
-  if (box.IsLayoutBlock() &&
-      ToLayoutBlock(box).UseLogicalBottomMarginEdgeForInlineBlockBaseline())
-    return false;
-  if (box.IsWritingModeRoot())
-    return false;
-  return true;
-}
-
-}  // namespace
-
 NGConstraintSpace::NGConstraintSpace(
     WritingMode writing_mode,
     bool is_orthogonal_writing_mode_root,
@@ -146,16 +133,10 @@
     available_size.block_size = box.OverrideLogicalHeight();
     fixed_block = true;
   }
-  if (box.IsFlexItem()) {
-    LayoutUnit for_percentage =
+  if (box.IsFlexItem() && fixed_block) {
+    fixed_block_is_definite =
         ToLayoutFlexibleBox(box.Parent())
-            ->ChildLogicalHeightForPercentageResolution(box);
-    fixed_block_is_definite = for_percentage != LayoutUnit(-1);
-    if (fixed_block_is_definite) {
-      DCHECK_EQ(available_size.block_size,
-                for_percentage + box.BorderAndPaddingLogicalHeight() +
-                    box.ScrollbarLogicalHeight());
-    }
+            ->UseOverrideLogicalHeightForPerentageResolution(box);
   }
 
   bool is_new_fc = true;
@@ -178,16 +159,20 @@
 
   NGConstraintSpaceBuilder builder(writing_mode, initial_containing_block_size);
 
-  if (ShouldComputeBaseline(box)) {
+  if (!box.IsWritingModeRoot()) {
     FontBaseline baseline_type = IsHorizontalWritingMode(writing_mode)
                                      ? kAlphabeticBaseline
                                      : kIdeographicBaseline;
     // Add all types because we don't know which baselines will be requested.
-    builder
-        .AddBaselineRequest(
-            {NGBaselineAlgorithmType::kAtomicInline, baseline_type})
-        .AddBaselineRequest(
-            {NGBaselineAlgorithmType::kFirstLine, baseline_type});
+    bool synthesize_inline_block_baseline =
+        box.IsLayoutBlock() &&
+        ToLayoutBlock(box).UseLogicalBottomMarginEdgeForInlineBlockBaseline();
+    if (!synthesize_inline_block_baseline) {
+      builder.AddBaselineRequest(
+          {NGBaselineAlgorithmType::kAtomicInline, baseline_type});
+    }
+    builder.AddBaselineRequest(
+        {NGBaselineAlgorithmType::kFirstLine, baseline_type});
   }
 
   return builder.SetAvailableSize(available_size)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc b/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
index 03b89dc..f33be5c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
@@ -54,7 +54,9 @@
   EXPECT_TRUE(result);
 
   String expected_text("Hello World!");
-  EXPECT_EQ(expected_text, ToNGInlineNode(node.FirstChild()).Text(0, 12));
+  NGInlineNode first_child = ToNGInlineNode(node.FirstChild());
+  EXPECT_EQ(expected_text,
+            StringView(first_child.ItemsData(false).text_content, 0, 12));
 }
 
 TEST_F(NGInlineLayoutTest, BlockWithTextAndAtomicInline) {
@@ -80,7 +82,9 @@
   String expected_text("Hello ");
   expected_text.append(kObjectReplacementCharacter);
   expected_text.append(".");
-  EXPECT_EQ(expected_text, ToNGInlineNode(node.FirstChild()).Text(0, 8));
+  NGInlineNode first_child = ToNGInlineNode(node.FirstChild());
+  EXPECT_EQ(expected_text,
+            StringView(first_child.ItemsData(false).text_content, 0, 8));
 
   // Delete the line box tree to avoid leaks in the test.
   block_flow->DeleteLineBoxTree();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc
index 15978cd..ab19f30 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc
@@ -42,7 +42,8 @@
   }
 
   if (node.IsInline()) {
-    for (const NGInlineItem& inline_item : ToNGInlineNode(node).Items()) {
+    const auto& items = ToNGInlineNode(node).ItemsData(false).items;
+    for (const NGInlineItem& inline_item : items) {
       string_builder->Append(indent_builder.ToString());
       string_builder->Append(inline_item.ToString());
       string_builder->Append("\n");
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index d154a67fe..84ff0cc 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -178,7 +178,6 @@
 
 ResourceRequest FrameLoader::ResourceRequestForReload(
     FrameLoadType frame_load_type,
-    const KURL& override_url,
     ClientRedirectPolicy client_redirect_policy) {
   DCHECK(IsReloadLoadType(frame_load_type));
   const auto cache_mode = frame_load_type == kFrameLoadTypeReloadBypassingCache
@@ -205,10 +204,6 @@
         frame_->GetDocument()->OutgoingReferrer()));
   }
 
-  if (!override_url.IsEmpty()) {
-    request.SetURL(override_url);
-    request.ClearHTTPReferrer();
-  }
   request.SetSkipServiceWorker(frame_load_type ==
                                kFrameLoadTypeReloadBypassingCache);
   return request;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 851a1bd2..b334584 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -85,7 +85,6 @@
 
   ResourceRequest ResourceRequestForReload(
       FrameLoadType,
-      const KURL& override_url = KURL(),
       ClientRedirectPolicy = ClientRedirectPolicy::kNotClientRedirect);
 
   ProgressTracker& Progress() const { return *progress_tracker_; }
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
index 2ef9b731c..0b41dcdf 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
@@ -18,12 +18,13 @@
 namespace blink {
 
 ModuleTreeLinker* ModuleTreeLinker::Fetch(const KURL& url,
+                                          const KURL& base_url,
                                           const ScriptFetchOptions& options,
                                           Modulator* modulator,
                                           ModuleTreeLinkerRegistry* registry,
                                           ModuleTreeClient* client) {
   ModuleTreeLinker* fetcher = new ModuleTreeLinker(modulator, registry, client);
-  fetcher->FetchRoot(url, options);
+  fetcher->FetchRoot(url, base_url, options);
   return fetcher;
 }
 
@@ -131,6 +132,7 @@
 
 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-module-script-tree
 void ModuleTreeLinker::FetchRoot(const KURL& original_url,
+                                 const KURL& base_url,
                                  const ScriptFetchOptions& options) {
 #if DCHECK_IS_ON()
   original_url_ = original_url;
@@ -142,9 +144,10 @@
   KURL url = original_url;
   // <spec
   // href="https://github.com/drufball/layered-apis/blob/master/spec.md#fetch-a-module-script-graph"
-  // step="1">Set url to the layered API fetching URL for url.</spec>
+  // step="1">Set url to the layered API fetching URL given url and the current
+  // settings object's API base URL.</spec>
   if (RuntimeEnabledFeatures::LayeredAPIEnabled())
-    url = blink::layered_api::ResolveFetchingURL(url);
+    url = blink::layered_api::ResolveFetchingURL(url, base_url);
 
 #if DCHECK_IS_ON()
   url_ = url;
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.h b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.h
index ee7d659130..771ed40 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.h
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.h
@@ -35,7 +35,11 @@
 class CORE_EXPORT ModuleTreeLinker final : public SingleModuleClient {
  public:
   // https://html.spec.whatwg.org/#fetch-a-module-script-tree
+  //
+  // TODO(hiroshige): |base_url| is used only for Layered APIs and will be
+  // removed soon once an upcoming spec change lands.
   static ModuleTreeLinker* Fetch(const KURL&,
+                                 const KURL& base_url,
                                  const ScriptFetchOptions&,
                                  Modulator*,
                                  ModuleTreeLinkerRegistry*,
@@ -75,7 +79,7 @@
 #endif
   void AdvanceState(State);
 
-  void FetchRoot(const KURL&, const ScriptFetchOptions&);
+  void FetchRoot(const KURL&, const KURL& base_url, const ScriptFetchOptions&);
   void FetchRootInline(ModuleScript*);
 
   // Steps 1--2 of [IMSGF].
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.cc
index 47dd025..e7aafad 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.cc
@@ -22,11 +22,12 @@
 
 ModuleTreeLinker* ModuleTreeLinkerRegistry::Fetch(
     const KURL& url,
+    const KURL& base_url,
     const ScriptFetchOptions& options,
     Modulator* modulator,
     ModuleTreeClient* client) {
   ModuleTreeLinker* fetcher =
-      ModuleTreeLinker::Fetch(url, options, modulator, this, client);
+      ModuleTreeLinker::Fetch(url, base_url, options, modulator, this, client);
   DCHECK(fetcher->IsFetching());
   active_tree_linkers_.insert(fetcher);
   return fetcher;
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.h b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.h
index 64e8084..fc19589 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.h
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_registry.h
@@ -34,6 +34,7 @@
   }
 
   ModuleTreeLinker* Fetch(const KURL&,
+                          const KURL& base_url,
                           const ScriptFetchOptions&,
                           Modulator*,
                           ModuleTreeClient*);
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
index 8b7a1ee..a6662c3 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
@@ -199,7 +199,7 @@
 
   KURL url("http://example.com/root.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -218,7 +218,7 @@
 
   KURL url("http://example.com/root.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -241,7 +241,7 @@
 
   KURL url("http://example.com/root.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -265,7 +265,7 @@
 
   KURL url("http://example.com/root.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -302,7 +302,7 @@
 
   KURL url("http://example.com/root.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -358,7 +358,7 @@
 
   KURL url("http://example.com/depth1.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
@@ -381,7 +381,7 @@
 
   KURL url("http://example.com/a.js");
   TestModuleTreeClient* client = new TestModuleTreeClient;
-  registry->Fetch(url, ScriptFetchOptions(), GetModulator(), client);
+  registry->Fetch(url, NullURL(), ScriptFetchOptions(), GetModulator(), client);
 
   EXPECT_FALSE(client->WasNotifyFinished())
       << "ModuleTreeLinker should always finish asynchronously.";
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.cc b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
index 94b6516..c062d49 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.cc
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
@@ -259,7 +259,7 @@
     std::unique_ptr<UserGestureIndicator> gesture_indicator =
         CreateUserGestureIndicator();
     ResourceRequest resource_request = frame->Loader().ResourceRequestForReload(
-        kFrameLoadTypeReload, KURL(), ClientRedirectPolicy::kClientRedirect);
+        kFrameLoadTypeReload, ClientRedirectPolicy::kClientRedirect);
     if (resource_request.IsNull())
       return;
     FrameLoadRequest request = FrameLoadRequest(nullptr, resource_request);
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index c2406ea..825df41 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -445,10 +445,11 @@
 
   data.selection_start_offset = 0;
   // HitTestResult::isSelected() ensures clean layout by performing a hit test.
-  // If source_type is |kMenuSourceAdjustSelectionReset| we know the original
-  // HitTestResult in SelectionController passed the inside check already, so
-  // let it pass.
-  if (r.IsSelected() || source_type == kMenuSourceAdjustSelectionReset) {
+  // If source_type is |kMenuSourceAdjustSelection| or
+  // |kMenuSourceAdjustSelectionReset| we know the original HitTestResult in
+  // SelectionController passed the inside check already, so let it pass.
+  if (r.IsSelected() || source_type == kMenuSourceAdjustSelection ||
+      source_type == kMenuSourceAdjustSelectionReset) {
     data.selected_text = selected_frame->SelectedText();
     WebRange range =
         selected_frame->GetInputMethodController().GetSelectionOffsets();
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index cec17a4..00b7de34 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -318,6 +318,13 @@
 void CompositedLayerMapping::UpdateFilters() {
   CompositorFilterOperations operations;
   OwningLayer().UpdateCompositorFilterOperationsForFilter(operations);
+
+  // If the image violates some feature policy optimized image policies, render
+  // with inverted color.
+  if (GetLayoutObject().IsLayoutImage() &&
+      ToLayoutImage(GetLayoutObject()).ShouldInvertColor())
+    operations.AppendInvertFilter(1.0f);
+
   graphics_layer_->SetFilters(std::move(operations));
 }
 
@@ -3369,27 +3376,19 @@
   LayoutRect graphics_layer_bounds_in_root_view_space(
       graphics_layer_bounds_in_object_space);
 
+  // MapToVisualRectInAncestorSpace is exclusive of the scroll and clip on the
+  // ancestor, so we map to nullptr instead of |root_view| to include these.
   anchor_layout_object->MapToVisualRectInAncestorSpace(
-      root_view, graphics_layer_bounds_in_root_view_space);
+      nullptr, graphics_layer_bounds_in_root_view_space);
 
-  // In RLS, the root_view is scrolled. However, MapToVisualRectInAncestorSpace
-  // doesn't account for this scroll, since it earlies out as soon as we reach
-  // this ancestor. That is, it only maps to the space of the root_view, not
-  // accounting for the fact that the root_view itself can be scrolled. If the
-  // root_view is our anchor_layout_object, then this extra offset is counted in
-  // offset_from_anchor_layout_object. In other cases, we need to account for it
-  // here. Otherwise, the paint clip below might clip the whole (visible) rect
-  // out.
-  if (RuntimeEnabledFeatures::RootLayerScrollingEnabled() &&
-      root_view != anchor_layout_object) {
-    if (auto* scrollable_area = root_view->GetScrollableArea()) {
-      graphics_layer_bounds_in_root_view_space.MoveBy(
-          -scrollable_area->VisibleContentRect().Location());
-    }
-  }
-
+  // MapToVisualRectInAncestorSpace will not clip if the anchor is the root
+  // view, because the rect is assumed to already be in the clipped space of
+  // the root view. We need to manually apply the root view's clip in this case.
   FloatRect visible_content_rect(graphics_layer_bounds_in_root_view_space);
-  root_view->GetFrameView()->ClipPaintRect(&visible_content_rect);
+  if (anchor_layout_object == root_view ||
+      !RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
+    root_view->GetFrameView()->ClipPaintRect(&visible_content_rect);
+  }
 
   FloatRect enclosing_graphics_layer_bounds(
       EnclosingIntRect(graphics_layer_bounds));
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
index c4c8690..b7ab205 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/platform/web_content_layer.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
+#include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -986,6 +987,32 @@
   EXPECT_EQ(IntRect(1000, 0, 4400, 300), RecomputeInterestRect(graphics_layer));
 }
 
+TEST_P(CompositedLayerMappingTest, ScrolledFixedPositionInterestRect) {
+  GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
+      true);
+  SetBodyInnerHTML(R"HTML(
+    <style>body { margin:0; } ::-webkit-scrollbar { display:none; }</style>
+    <div id="fixed" style="position: fixed;">
+      <div style="background: blue; width: 30px; height: 30px;"></div>
+      <div style="position: absolute; transform: translateY(-4500px);
+          top: 0; left: 0; width: 100px; height: 100px;"></div>
+    </div>
+    <div id="forcescroll" style="height: 2000px;"></div>
+  )HTML");
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  auto* fixed = GetDocument().getElementById("fixed")->GetLayoutObject();
+  auto* graphics_layer = fixed->EnclosingLayer()->GraphicsLayerBacking(fixed);
+  EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer));
+
+  GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
+      ScrollOffset(0.0, 200.0), kProgrammaticScroll);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  // Because the fixed element does not scroll, the interest rect is unchanged.
+  EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer));
+}
+
 TEST_P(CompositedLayerMappingTest,
        ScrollingContentsAndForegroundLayerPaintingPhase) {
   GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
@@ -2687,4 +2714,45 @@
             squashed->GraphicsLayerBacking()->GetPosition());
 }
 
+TEST_P(CompositedLayerMappingTest, ImageWithInvertFilterLayer) {
+  SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>");
+  ToLayoutImage(GetLayoutObjectByElementId("image"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  cc::FilterOperations filters;
+  filters.Append(cc::FilterOperation::CreateInvertFilter(1.0f));
+  EXPECT_EQ(filters, ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
+                         ->Layer()
+                         ->GraphicsLayerBacking()
+                         ->PlatformLayer()
+                         ->CcLayer()
+                         ->filters());
+}
+
+TEST_P(CompositedLayerMappingTest, ImageWithInvertFilterLayerUpdated) {
+  SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>");
+  ToLayoutImage(GetLayoutObjectByElementId("image"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  cc::FilterOperations filters0, filters1;
+  filters0.Append(cc::FilterOperation::CreateInvertFilter(1.0f));
+  EXPECT_EQ(filters0,
+            ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
+                ->Layer()
+                ->GraphicsLayerBacking()
+                ->PlatformLayer()
+                ->CcLayer()
+                ->filters());
+  ToLayoutImage(GetLayoutObjectByElementId("image"))
+      ->UpdateShouldInvertColor(false);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  EXPECT_EQ(filters1,
+            ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
+                ->Layer()
+                ->GraphicsLayerBacking()
+                ->PlatformLayer()
+                ->CcLayer()
+                ->filters());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index f78ed48..250714d 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/layout/fragmentainer_iterator.h"
+#include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_table_row.h"
 #include "third_party/blink/renderer/core/layout/layout_table_section.h"
@@ -909,8 +910,12 @@
 
 static bool NeedsFilter(const LayoutObject& object) {
   // TODO(trchen): SVG caches filters in SVGResources. Implement it.
-  return object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
-         (object.StyleRef().HasFilter() || object.HasReflection());
+  if (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
+      (object.StyleRef().HasFilter() || object.HasReflection()))
+    return true;
+  if (object.IsLayoutImage() && ToLayoutImage(object).ShouldInvertColor())
+    return true;
+  return false;
 }
 
 void FragmentPaintPropertyTreeBuilder::UpdateFilter() {
@@ -923,12 +928,24 @@
       state.local_transform_space = context_.current.transform;
       state.paint_offset = FloatPoint(context_.current.paint_offset);
 
-      // Try to use the cached filter.
-      if (properties_->Filter())
-        state.filter = properties_->Filter()->Filter();
-      auto& layer = *ToLayoutBoxModelObject(object_).Layer();
-      layer.UpdateCompositorFilterOperationsForFilter(state.filter);
-      layer.ClearFilterOnEffectNodeDirty();
+      auto* layer = ToLayoutBoxModelObject(object_).Layer();
+      if (layer) {
+        // Try to use the cached filter.
+        if (properties_->Filter())
+          state.filter = properties_->Filter()->Filter();
+
+        if (object_.IsLayoutImage() &&
+            ToLayoutImage(object_).ShouldInvertColor())
+          state.filter.AppendInvertFilter(1.0f);
+
+        layer->UpdateCompositorFilterOperationsForFilter(state.filter);
+        layer->ClearFilterOnEffectNodeDirty();
+      } else {
+        DCHECK(object_.IsLayoutImage() &&
+               ToLayoutImage(object_).ShouldInvertColor());
+        state.filter = CompositorFilterOperations();
+        state.filter.AppendInvertFilter(1.0f);
+      }
 
       // The CSS filter spec didn't specify how filters interact with overflow
       // clips. The implementation here mimics the old Blink/WebKit behavior for
@@ -1075,7 +1092,8 @@
   if (!NeedsPaintPropertyUpdate())
     return;
 
-  if (!object_.HasLayer() && !NeedsPaintOffsetTranslation(object_)) {
+  if (!object_.HasLayer() && !NeedsPaintOffsetTranslation(object_) &&
+      !NeedsFilter(object_)) {
     fragment_data_.ClearLocalBorderBoxProperties();
   } else {
     PropertyTreeState local_border_box =
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 0c27a04..0d32bc8 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h"
 
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
+#include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_tree_as_text.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
@@ -5499,4 +5500,70 @@
   EXPECT_EQ(1u, NumFragments(fixed_child));
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilter) {
+  SetBodyInnerHTML(R"HTML(
+    <img id='img' src='x'>
+  )HTML");
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  const auto* filters = PaintPropertiesForElement("img")->Filter();
+  ASSERT_NE(nullptr, filters);
+  CompositorFilterOperations filters_expect;
+  filters_expect.AppendInvertFilter(1.0f);
+  EXPECT_EQ(filters_expect, filters->Filter());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilterUpdated) {
+  SetBodyInnerHTML(R"HTML(
+    <img id='img' src='x'>
+  )HTML");
+
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  const auto* filters = PaintPropertiesForElement("img")->Filter();
+  ASSERT_NE(nullptr, filters);
+  CompositorFilterOperations filters_expect;
+  filters_expect.AppendInvertFilter(1.0f);
+  EXPECT_EQ(filters_expect, filters->Filter());
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(false);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  EXPECT_EQ(nullptr, PaintPropertiesForElement("img"));
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilter) {
+  SetBodyInnerHTML(R"HTML(
+    <img id='img' style='position: relative;' src='x'>
+  )HTML");
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  const auto* filters = PaintPropertiesForElement("img")->Filter();
+  ASSERT_NE(nullptr, filters);
+  CompositorFilterOperations filters_expect;
+  filters_expect.AppendInvertFilter(1.0f);
+  EXPECT_EQ(filters_expect, filters->Filter());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilterUpdated) {
+  SetBodyInnerHTML(R"HTML(
+    <img id='img' style='position: relative;' src='x'>
+  )HTML");
+
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  const auto* filters = PaintPropertiesForElement("img")->Filter();
+  ASSERT_NE(nullptr, filters);
+  CompositorFilterOperations filters_expect;
+  filters_expect.AppendInvertFilter(1.0f);
+  EXPECT_EQ(filters_expect, filters->Filter());
+  ToLayoutImage(GetLayoutObjectByElementId("img"))
+      ->UpdateShouldInvertColor(false);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  EXPECT_EQ(nullptr, PaintPropertiesForElement("img"));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index df2d892..0d50e827 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -86,40 +86,56 @@
     base::Optional<RoundedInnerRectClipper> clipper;
     base::Optional<ScopedPaintChunkProperties> chunk_properties;
     bool completely_clipped_out = false;
-    if (layout_replaced_.Style()->HasBorderRadius()) {
-      if (border_rect.IsEmpty()) {
-        completely_clipped_out = true;
-      } else if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
-        if (!layout_replaced_.IsSVGRoot()) {
+
+    if (layout_replaced_.Style()->HasBorderRadius() && border_rect.IsEmpty())
+      completely_clipped_out = true;
+
+    if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+      if (!layout_replaced_.IsSVGRoot()) {
+        if (layout_replaced_.Style()->HasBorderRadius()) {
           if (const auto* fragment =
                   paint_info.FragmentToPaint(layout_replaced_)) {
-            const auto* properties = fragment->PaintProperties();
-            DCHECK(properties && properties->InnerBorderRadiusClip());
+            const auto* paint_properties = fragment->PaintProperties();
+            DCHECK(paint_properties &&
+                   paint_properties->InnerBorderRadiusClip());
             chunk_properties.emplace(
                 local_paint_info.context.GetPaintController(),
-                properties->InnerBorderRadiusClip(), layout_replaced_,
+                paint_properties->InnerBorderRadiusClip(), layout_replaced_,
+                DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
+          }
+        } else if (!layout_replaced_.HasLayer() ||
+                   !layout_replaced_.Layer()->IsSelfPaintingLayer()) {
+          // The only use case of this is to apply color-inversion filter for
+          // images violating feature policy optimized image policies.
+          if (layout_replaced_.FirstFragment().HasLocalBorderBoxProperties()) {
+            chunk_properties.emplace(
+                local_paint_info.context.GetPaintController(),
+                layout_replaced_.FirstFragment().LocalBorderBoxProperties(),
+                layout_replaced_,
                 DisplayItem::PaintPhaseToDrawingType(local_paint_info.phase));
           }
         }
-      } else if (ShouldApplyViewportClip(layout_replaced_)) {
-        // Push a clip if we have a border radius, since we want to round the
-        // foreground content that gets painted.
-        FloatRoundedRect rounded_inner_rect =
-            layout_replaced_.Style()->GetRoundedInnerBorderFor(
-                border_rect,
-                LayoutRectOutsets(-(layout_replaced_.PaddingTop() +
-                                    layout_replaced_.BorderTop()),
-                                  -(layout_replaced_.PaddingRight() +
-                                    layout_replaced_.BorderRight()),
-                                  -(layout_replaced_.PaddingBottom() +
-                                    layout_replaced_.BorderBottom()),
-                                  -(layout_replaced_.PaddingLeft() +
-                                    layout_replaced_.BorderLeft())),
-                true, true);
-
-        clipper.emplace(layout_replaced_, local_paint_info, border_rect,
-                        rounded_inner_rect, kApplyToDisplayList);
       }
+    } else if (!completely_clipped_out &&
+               layout_replaced_.Style()->HasBorderRadius() &&
+               ShouldApplyViewportClip(layout_replaced_)) {
+      // Push a clip if we have a border radius, since we want to round the
+      // foreground content that gets painted.
+      FloatRoundedRect rounded_inner_rect =
+          layout_replaced_.Style()->GetRoundedInnerBorderFor(
+              border_rect,
+              LayoutRectOutsets(-(layout_replaced_.PaddingTop() +
+                                  layout_replaced_.BorderTop()),
+                                -(layout_replaced_.PaddingRight() +
+                                  layout_replaced_.BorderRight()),
+                                -(layout_replaced_.PaddingBottom() +
+                                  layout_replaced_.BorderBottom()),
+                                -(layout_replaced_.PaddingLeft() +
+                                  layout_replaced_.BorderLeft())),
+              true, true);
+
+      clipper.emplace(layout_replaced_, local_paint_info, border_rect,
+                      rounded_inner_rect, kApplyToDisplayList);
     }
 
     if (!completely_clipped_out) {
diff --git a/third_party/blink/renderer/core/script/layered_api.cc b/third_party/blink/renderer/core/script/layered_api.cc
index b6796248..55b3c54 100644
--- a/third_party/blink/renderer/core/script/layered_api.cc
+++ b/third_party/blink/renderer/core/script/layered_api.cc
@@ -58,7 +58,7 @@
 }  // namespace
 
 // https://github.com/drufball/layered-apis/blob/master/spec.md#user-content-layered-api-fetching-url
-KURL ResolveFetchingURL(const KURL& url) {
+KURL ResolveFetchingURL(const KURL& url, const KURL& base_url) {
   // <spec step="1">If url's scheme is not "std", return url.</spec>
   if (!url.ProtocolIs(kStdScheme))
     return url;
@@ -96,8 +96,9 @@
   if (fallback.IsNull())
     return NullURL();
 
-  // <spec step="7">Return the result of parsing fallback.</spec>
-  return KURL(NullURL(), fallback);
+  // <spec step="7">Return the result of parsing fallback with the base URL
+  // baseURLForFallback.</spec>
+  return KURL(base_url, fallback);
 }
 
 KURL GetInternalURL(const KURL& url) {
diff --git a/third_party/blink/renderer/core/script/layered_api.h b/third_party/blink/renderer/core/script/layered_api.h
index f9d3599..87f8e59 100644
--- a/third_party/blink/renderer/core/script/layered_api.h
+++ b/third_party/blink/renderer/core/script/layered_api.h
@@ -21,7 +21,7 @@
 // href="https://github.com/drufball/layered-apis/blob/master/spec.md#user-content-layered-api-fetching-url">
 // This operation maps URLs of the form std:x|y to either std:x or y
 // URLs.</spec>
-CORE_EXPORT KURL ResolveFetchingURL(const KURL&);
+CORE_EXPORT KURL ResolveFetchingURL(const KURL&, const KURL& base_url);
 
 // Returns std-internal://x/index.js if the URL is Layered API, or null URL
 // otherwise (not specced).
diff --git a/third_party/blink/renderer/core/script/layered_api_test.cc b/third_party/blink/renderer/core/script/layered_api_test.cc
index 0f056fc..ae78031 100644
--- a/third_party/blink/renderer/core/script/layered_api_test.cc
+++ b/third_party/blink/renderer/core/script/layered_api_test.cc
@@ -13,18 +13,33 @@
 namespace {
 
 TEST(LayeredAPITest, ResolveFetchingURL) {
-  EXPECT_EQ(ResolveFetchingURL(KURL("https://example.com/")),
+  KURL base_url("https://example.com/base/path/");
+
+  EXPECT_EQ(ResolveFetchingURL(KURL("https://example.com/"), base_url),
             KURL("https://example.com/"));
-  EXPECT_EQ(ResolveFetchingURL(KURL("std:blank")), KURL("std:blank"));
-  EXPECT_EQ(ResolveFetchingURL(KURL("std:blank|https://fallback.example.com/")),
+
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:blank"), base_url), KURL("std:blank"));
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:blank|https://fallback.example.com/"),
+                               base_url),
             KURL("std:blank"));
-  EXPECT_EQ(ResolveFetchingURL(KURL("std:blank|https://:invalid-url")),
-            KURL("std:blank"));
-  EXPECT_EQ(ResolveFetchingURL(KURL("std:none")), NullURL());
-  EXPECT_EQ(ResolveFetchingURL(KURL("std:none|https://fallback.example.com/")),
+  EXPECT_EQ(
+      ResolveFetchingURL(KURL("std:blank|https://:invalid-url"), base_url),
+      KURL("std:blank"));
+
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:none"), base_url), NullURL());
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:none|https://fallback.example.com/"),
+                               base_url),
             KURL("https://fallback.example.com/"));
   EXPECT_FALSE(
-      ResolveFetchingURL(KURL("std:none|https://:invalid-url")).IsValid());
+      ResolveFetchingURL(KURL("std:none|https://:invalid-url"), base_url)
+          .IsValid());
+
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:none|fallback.js"), base_url),
+            KURL("https://example.com/base/path/fallback.js"));
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:none|./fallback.js"), base_url),
+            KURL("https://example.com/base/path/fallback.js"));
+  EXPECT_EQ(ResolveFetchingURL(KURL("std:none|/fallback.js"), base_url),
+            KURL("https://example.com/fallback.js"));
 }
 
 TEST(LayeredAPITest, GetInternalURL) {
diff --git a/third_party/blink/renderer/core/script/modulator.cc b/third_party/blink/renderer/core/script/modulator.cc
index c958aee..ab4eca3 100644
--- a/third_party/blink/renderer/core/script/modulator.cc
+++ b/third_party/blink/renderer/core/script/modulator.cc
@@ -92,9 +92,9 @@
     // href="https://github.com/drufball/layered-apis/blob/master/spec.md#resolve-a-module-specifier"
     // step="1">Let parsed be the result of applying the URL parser to
     // specifier. If parsed is not failure, then return the layered API fetching
-    // URL for parsed.</spec>
+    // URL given parsed and script's base URL.</spec>
     if (RuntimeEnabledFeatures::LayeredAPIEnabled())
-      return blink::layered_api::ResolveFetchingURL(url);
+      return blink::layered_api::ResolveFetchingURL(url, base_url);
 
     return url;
   }
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc
index 93a01339..0793bc4 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.cc
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -67,7 +67,8 @@
   // of this algorithm specified custom perform the fetch steps, pass those
   // along as well.</spec>
 
-  tree_linker_registry_->Fetch(url, options, this, client);
+  tree_linker_registry_->Fetch(url, GetExecutionContext()->BaseURL(), options,
+                               this, client);
 
   // <spec label="fetch-a-module-script-tree" step="3">When the internal module
   // script graph fetching procedure asynchronously completes with result,
diff --git a/third_party/blink/renderer/core/script/script_loader.h b/third_party/blink/renderer/core/script/script_loader.h
index 3493263b..de6e46e 100644
--- a/third_party/blink/renderer/core/script/script_loader.h
+++ b/third_party/blink/renderer/core/script/script_loader.h
@@ -138,6 +138,10 @@
                bool is_evaluated,
                bool created_during_document_write);
 
+  void SetAsyncExecTypeForTesting(ScriptRunner::AsyncExecutionType type) {
+    async_exec_type_ = type;
+  }
+
  private:
   bool IgnoresLoadRequest() const;
   bool IsScriptForEventSupported() const;
diff --git a/third_party/blink/renderer/core/script/script_runner.cc b/third_party/blink/renderer/core/script/script_runner.cc
index 0709b30..8073a0c8 100644
--- a/third_party/blink/renderer/core/script/script_runner.cc
+++ b/third_party/blink/renderer/core/script/script_runner.cc
@@ -269,7 +269,9 @@
   DCHECK(!is_suspended_);
   DCHECK(script_loader);
 
-  // Currently, we support streaming only for async scripts.
+  // Currently, we stream only async scripts in this function.
+  // Note: HTMLParserScriptRunner kicks streaming for deferred or blocking
+  // scripts.
   DCHECK(pending_async_scripts_.find(script_loader) !=
              pending_async_scripts_.end() ||
          std::find(async_scripts_to_execute_soon_.begin(),
diff --git a/third_party/blink/renderer/core/script/script_runner_test.cc b/third_party/blink/renderer/core/script/script_runner_test.cc
index eb79383a..bbc2e6e 100644
--- a/third_party/blink/renderer/core/script/script_runner_test.cc
+++ b/third_party/blink/renderer/core/script/script_runner_test.cc
@@ -37,17 +37,39 @@
   MOCK_CONST_METHOD0(UrlForTracing, KURL());
   MOCK_METHOD0(RemoveFromMemoryCache, void());
 
-  MOCK_CONST_METHOD0(IsCurrentlyStreaming, bool());
-  // The currently released googletest cannot mock methods with C++ move-only
-  // types like std::unique_ptr. This has been promised to be fixed in a
-  // future release of googletest, but for now we use the recommended
-  // workaround of 'bumping' the method-to-be-mocked to another method.
+  enum class State {
+    kStreamingNotReady,
+    kReadyToBeStreamed,
+    kStreaming,
+    kStreamingFinished,
+  };
+
+  bool IsCurrentlyStreaming() const override {
+    return state_ == State::kStreaming;
+  }
+
+  void PrepareForStreaming() {
+    DCHECK_EQ(state_, State::kStreamingNotReady);
+    state_ = State::kReadyToBeStreamed;
+  }
+
   bool StartStreamingIfPossible(ScriptStreamer::Type type,
                                 base::OnceClosure closure) override {
-    return DoStartStreamingIfPossible(type, &closure);
+    if (state_ != State::kReadyToBeStreamed)
+      return false;
+
+    state_ = State::kStreaming;
+    streaming_finished_callback_ = std::move(closure);
+    return true;
   }
-  MOCK_METHOD2(DoStartStreamingIfPossible,
-               bool(ScriptStreamer::Type, base::OnceClosure*));
+
+  void SimulateStreamingEnd() {
+    DCHECK_EQ(state_, State::kStreaming);
+    state_ = State::kStreamingFinished;
+    std::move(streaming_finished_callback_).Run();
+  }
+
+  State state() const { return state_; }
 
  protected:
   MOCK_METHOD0(DisposeInternal, void());
@@ -55,67 +77,50 @@
 
  private:
   MockPendingScript() : PendingScript(nullptr, TextPosition()) {}
+
+  State state_ = State::kStreamingNotReady;
+  base::OnceClosure streaming_finished_callback_;
 };
 
 class MockScriptLoader final : public ScriptLoader {
  public:
-  static MockScriptLoader* Create() {
-    return (new MockScriptLoader())->SetupForNonStreaming();
+  static MockScriptLoader* CreateInOrder() {
+    auto* loader = new MockScriptLoader();
+    loader->SetAsyncExecTypeForTesting(ScriptRunner::kInOrder);
+    return loader;
   }
+  static MockScriptLoader* CreateAsync() {
+    auto* loader = new MockScriptLoader();
+    loader->SetAsyncExecTypeForTesting(ScriptRunner::kAsync);
+    loader->mock_pending_script_if_script_is_async_ =
+        MockPendingScript::Create();
+    return loader;
+  }
+
   ~MockScriptLoader() override {}
 
   MOCK_METHOD0(Execute, void());
   MOCK_CONST_METHOD0(IsReady, bool());
-  MOCK_METHOD0(GetPendingScriptIfScriptIsAsync, PendingScript*());
-
-  // Set up the mock for streaming. The closure passed in will receive the
-  // 'finish streaming' callback, so that the test case can control when
-  // the mock streaming has mock finished.
-  MockScriptLoader* SetupForStreaming(base::OnceClosure& finished_callback);
-  MockScriptLoader* SetupForNonStreaming();
 
   void Trace(blink::Visitor*) override;
 
+  PendingScript* GetPendingScriptIfScriptIsAsync() override {
+    return mock_pending_script_if_script_is_async_.Get();
+  }
+  MockPendingScript* GetMockPendingScript() {
+    return mock_pending_script_if_script_is_async_.Get();
+  }
+
  private:
   explicit MockScriptLoader()
       : ScriptLoader(MockScriptElementBase::Create(), false, false, false) {}
-  Member<MockPendingScript> mock_pending_script_;
+
+  Member<MockPendingScript> mock_pending_script_if_script_is_async_;
 };
 
 void MockScriptLoader::Trace(blink::Visitor* visitor) {
   ScriptLoader::Trace(visitor);
-  visitor->Trace(mock_pending_script_);
-}
-
-MockScriptLoader* MockScriptLoader::SetupForStreaming(
-    base::OnceClosure& finished_callback) {
-  mock_pending_script_ = MockPendingScript::Create();
-  SetupForNonStreaming();
-
-  // Mock the streaming functions and save the 'streaming done' callback
-  // into the callback done variable. This way, we can control when
-  // streamig is done.
-  EXPECT_CALL(*mock_pending_script_, DoStartStreamingIfPossible(_, _))
-      .WillOnce(Return(false))
-      .WillOnce(
-          Invoke([&finished_callback](ScriptStreamer::Type,
-                                      base::OnceClosure* callback) -> bool {
-            finished_callback = std::move(*callback);
-            return true;
-          }))
-      .WillRepeatedly(Return(false));
-  EXPECT_CALL(*mock_pending_script_, IsCurrentlyStreaming())
-      .WillRepeatedly(
-          Invoke([&finished_callback] { return !!finished_callback; }));
-  return this;
-}
-
-MockScriptLoader* MockScriptLoader::SetupForNonStreaming() {
-  EXPECT_CALL(*this, GetPendingScriptIfScriptIsAsync())
-      .WillRepeatedly(Invoke([this]() -> PendingScript* {
-        return this->mock_pending_script_.Get();
-      }));
-  return this;
+  visitor->Trace(mock_pending_script_if_script_is_async_);
 }
 
 class ScriptRunnerTest : public testing::Test {
@@ -144,7 +149,8 @@
 };
 
 TEST_F(ScriptRunnerTest, QueueSingleScript_Async) {
-  MockScriptLoader* script_loader = MockScriptLoader::Create();
+  auto* script_loader = MockScriptLoader::CreateAsync();
+
   script_runner_->QueueScriptForExecution(script_loader, ScriptRunner::kAsync);
   script_runner_->NotifyScriptReady(script_loader, ScriptRunner::kAsync);
 
@@ -153,7 +159,7 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder) {
-  MockScriptLoader* script_loader = MockScriptLoader::Create();
+  auto* script_loader = MockScriptLoader::CreateInOrder();
   script_runner_->QueueScriptForExecution(script_loader,
                                           ScriptRunner::kInOrder);
 
@@ -166,9 +172,9 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateInOrder();
+  auto* script_loader2 = MockScriptLoader::CreateInOrder();
+  auto* script_loader3 = MockScriptLoader::CreateInOrder();
 
   HeapVector<Member<MockScriptLoader>> script_loaders;
   script_loaders.push_back(script_loader1);
@@ -206,11 +212,11 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueMixedScripts) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader4 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader5 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateInOrder();
+  auto* script_loader2 = MockScriptLoader::CreateInOrder();
+  auto* script_loader3 = MockScriptLoader::CreateInOrder();
+  auto* script_loader4 = MockScriptLoader::CreateAsync();
+  auto* script_loader5 = MockScriptLoader::CreateAsync();
 
   script_runner_->QueueScriptForExecution(script_loader1,
                                           ScriptRunner::kInOrder);
@@ -258,16 +264,16 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueReentrantScript_Async) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateAsync();
+  auto* script_loader2 = MockScriptLoader::CreateAsync();
+  auto* script_loader3 = MockScriptLoader::CreateAsync();
 
   script_runner_->QueueScriptForExecution(script_loader1, ScriptRunner::kAsync);
   script_runner_->QueueScriptForExecution(script_loader2, ScriptRunner::kAsync);
   script_runner_->QueueScriptForExecution(script_loader3, ScriptRunner::kAsync);
   script_runner_->NotifyScriptReady(script_loader1, ScriptRunner::kAsync);
 
-  MockScriptLoader* script_loader = script_loader2;
+  auto* script_loader = script_loader2;
   EXPECT_CALL(*script_loader1, Execute())
       .WillOnce(Invoke([script_loader, this] {
         order_.push_back(1);
@@ -298,9 +304,9 @@
 }
 
 TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateInOrder();
+  auto* script_loader2 = MockScriptLoader::CreateInOrder();
+  auto* script_loader3 = MockScriptLoader::CreateInOrder();
 
   EXPECT_CALL(*script_loader1, IsReady()).WillRepeatedly(Return(true));
   EXPECT_CALL(*script_loader2, IsReady()).WillRepeatedly(Return(true));
@@ -352,7 +358,7 @@
     script_loaders[i] = nullptr;
 
   for (int i = 0; i < 20; i++) {
-    script_loaders[i] = MockScriptLoader::Create();
+    script_loaders[i] = MockScriptLoader::CreateAsync();
     EXPECT_CALL(*script_loaders[i], IsReady()).WillRepeatedly(Return(true));
 
     script_runner_->QueueScriptForExecution(script_loaders[i],
@@ -386,9 +392,9 @@
 }
 
 TEST_F(ScriptRunnerTest, ResumeAndSuspend_InOrder) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateInOrder();
+  auto* script_loader2 = MockScriptLoader::CreateInOrder();
+  auto* script_loader3 = MockScriptLoader::CreateInOrder();
 
   script_runner_->QueueScriptForExecution(script_loader1,
                                           ScriptRunner::kInOrder);
@@ -431,9 +437,9 @@
 }
 
 TEST_F(ScriptRunnerTest, ResumeAndSuspend_Async) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader3 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateAsync();
+  auto* script_loader2 = MockScriptLoader::CreateAsync();
+  auto* script_loader3 = MockScriptLoader::CreateAsync();
 
   script_runner_->QueueScriptForExecution(script_loader1, ScriptRunner::kAsync);
   script_runner_->QueueScriptForExecution(script_loader2, ScriptRunner::kAsync);
@@ -463,8 +469,8 @@
 }
 
 TEST_F(ScriptRunnerTest, LateNotifications) {
-  MockScriptLoader* script_loader1 = MockScriptLoader::Create();
-  MockScriptLoader* script_loader2 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateInOrder();
+  auto* script_loader2 = MockScriptLoader::CreateInOrder();
 
   EXPECT_CALL(*script_loader1, IsReady()).WillRepeatedly(Return(true));
   EXPECT_CALL(*script_loader2, IsReady()).WillRepeatedly(Return(true));
@@ -493,8 +499,8 @@
 }
 
 TEST_F(ScriptRunnerTest, TasksWithDeadScriptRunner) {
-  Persistent<MockScriptLoader> script_loader1 = MockScriptLoader::Create();
-  Persistent<MockScriptLoader> script_loader2 = MockScriptLoader::Create();
+  Persistent<MockScriptLoader> script_loader1 = MockScriptLoader::CreateAsync();
+  Persistent<MockScriptLoader> script_loader2 = MockScriptLoader::CreateAsync();
 
   EXPECT_CALL(*script_loader1, IsReady()).WillRepeatedly(Return(true));
   EXPECT_CALL(*script_loader2, IsReady()).WillRepeatedly(Return(true));
@@ -518,33 +524,36 @@
 }
 
 TEST_F(ScriptRunnerTest, TryStreamWhenEnqueingScript) {
-  Persistent<MockScriptLoader> script_loader1 = MockScriptLoader::Create();
+  auto* script_loader1 = MockScriptLoader::CreateAsync();
   EXPECT_CALL(*script_loader1, IsReady()).WillRepeatedly(Return(true));
   script_runner_->QueueScriptForExecution(script_loader1, ScriptRunner::kAsync);
 }
 
 TEST_F(ScriptRunnerTest, DontExecuteWhileStreaming) {
-  base::OnceClosure callback;
-  Persistent<MockScriptLoader> script_loader1 =
-      MockScriptLoader::Create()->SetupForStreaming(callback);
-  EXPECT_CALL(*script_loader1, IsReady()).WillRepeatedly(Return(true));
+  auto* script_loader = MockScriptLoader::CreateAsync();
+  EXPECT_CALL(*script_loader, IsReady()).WillRepeatedly(Return(false));
 
-  // Enqueue script & make it ready.
-  script_runner_->QueueScriptForExecution(script_loader1, ScriptRunner::kAsync);
-  script_runner_->NotifyScriptReady(script_loader1, ScriptRunner::kAsync);
+  // Enqueue script.
+  script_runner_->QueueScriptForExecution(script_loader, ScriptRunner::kAsync);
 
-  // We should have started streaming by now.
-  CHECK(callback);
+  // Simulate script load and mark the pending script as streaming ready.
+  EXPECT_CALL(*script_loader, IsReady()).WillRepeatedly(Return(true));
+  script_loader->GetMockPendingScript()->PrepareForStreaming();
+  script_runner_->NotifyScriptReady(script_loader, ScriptRunner::kAsync);
+
+  // ScriptLoader should have started streaming by now.
+  EXPECT_EQ(script_loader->GetMockPendingScript()->state(),
+            MockPendingScript::State::kStreaming);
 
   // Note that there is no expectation for ScriptLoader::Execute() yet,
   // so the mock will fail if it's called anyway.
   platform_->RunUntilIdle();
 
   // Finish streaming.
-  std::move(callback).Run();
+  script_loader->GetMockPendingScript()->SimulateStreamingEnd();
 
   // Now that streaming is finished, expect Execute() to be called.
-  EXPECT_CALL(*script_loader1, Execute()).Times(1);
+  EXPECT_CALL(*script_loader, Execute()).Times(1);
   platform_->RunUntilIdle();
 }
 
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index 92bf988e..efc23715 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -703,6 +703,7 @@
   "front_end/terminal/xterm.js/build/xterm.js",
   "front_end/test_runner/module.json",
   "front_end/test_runner/TestRunner.js",
+  "front_end/text_editor/autocompleteTooltip.css",
   "front_end/text_editor/cmdevtools.css",
   "front_end/text_editor/CodeMirrorTextEditor.js",
   "front_end/text_editor/CodeMirrorUtils.js",
diff --git a/third_party/blink/renderer/devtools/front_end/Tests.js b/third_party/blink/renderer/devtools/front_end/Tests.js
index add96169..d853042 100644
--- a/third_party/blink/renderer/devtools/front_end/Tests.js
+++ b/third_party/blink/renderer/devtools/front_end/Tests.js
@@ -1110,13 +1110,31 @@
     this.releaseControl();
   };
 
+  TestSuite.prototype.testDisposeEmptyBrowserContext = async function(url) {
+    this.takeControl();
+    const targetAgent = SDK.targetManager.mainTarget().targetAgent();
+    const {browserContextId} = await targetAgent.invoke_createBrowserContext();
+    const response1 = await targetAgent.invoke_getBrowserContexts();
+    this.assertEquals(response1.browserContextIds.length, 1);
+    await targetAgent.invoke_disposeBrowserContext({browserContextId});
+    const response2 = await targetAgent.invoke_getBrowserContexts();
+    this.assertEquals(response2.browserContextIds.length, 0);
+    this.releaseControl();
+  };
+
   TestSuite.prototype.testCreateBrowserContext = async function(url) {
     this.takeControl();
     const browserContextIds = [];
+    const targetAgent = SDK.targetManager.mainTarget().targetAgent();
 
     const target1 = await createIsolatedTarget(url);
     const target2 = await createIsolatedTarget(url);
 
+    const response = await targetAgent.invoke_getBrowserContexts();
+    this.assertEquals(response.browserContextIds.length, 2);
+    this.assertTrue(response.browserContextIds.includes(browserContextIds[0]));
+    this.assertTrue(response.browserContextIds.includes(browserContextIds[1]));
+
     await evalCode(target1, 'localStorage.setItem("page1", "page1")');
     await evalCode(target2, 'localStorage.setItem("page2", "page2")');
 
@@ -1125,13 +1143,12 @@
     this.assertEquals(await evalCode(target2, 'localStorage.getItem("page1")'), null);
     this.assertEquals(await evalCode(target2, 'localStorage.getItem("page2")'), 'page2');
 
-    this.assertEquals(await disposeBrowserContext(browserContextIds[0]), false);
-    this.assertEquals(await disposeBrowserContext(browserContextIds[1]), false);
-
-    await closeTarget(target1);
-    await closeTarget(target2);
-    this.assertEquals(await disposeBrowserContext(browserContextIds[0]), true);
-    this.assertEquals(await disposeBrowserContext(browserContextIds[1]), true);
+    const removedTargets = [];
+    SDK.targetManager.observeTargets({targetAdded: () => {}, targetRemoved: target => removedTargets.push(target)});
+    await Promise.all([disposeBrowserContext(browserContextIds[0]), disposeBrowserContext(browserContextIds[1])]);
+    this.assertEquals(removedTargets.length, 2);
+    this.assertEquals(removedTargets.indexOf(target1) !== -1, true);
+    this.assertEquals(removedTargets.indexOf(target2) !== -1, true);
 
     this.releaseControl();
 
@@ -1140,7 +1157,6 @@
      * @return {!Promise<!SDK.Target>}
      */
     async function createIsolatedTarget(url) {
-      const targetAgent = SDK.targetManager.mainTarget().targetAgent();
       const {browserContextId} = await targetAgent.invoke_createBrowserContext();
       browserContextIds.push(browserContextId);
 
@@ -1154,15 +1170,9 @@
       return target;
     }
 
-    async function closeTarget(target) {
-      const targetAgent = SDK.targetManager.mainTarget().targetAgent();
-      await targetAgent.invoke_closeTarget({targetId: target.id()});
-    }
-
     async function disposeBrowserContext(browserContextId) {
       const targetAgent = SDK.targetManager.mainTarget().targetAgent();
-      const {success} = await targetAgent.invoke_disposeBrowserContext({browserContextId});
-      return success;
+      await targetAgent.invoke_disposeBrowserContext({browserContextId});
     }
 
     async function evalCode(target, code) {
diff --git a/third_party/blink/renderer/devtools/front_end/changes/ChangesSidebar.js b/third_party/blink/renderer/devtools/front_end/changes/ChangesSidebar.js
index 5c01d53..e5cb9bd 100644
--- a/third_party/blink/renderer/devtools/front_end/changes/ChangesSidebar.js
+++ b/third_party/blink/renderer/devtools/front_end/changes/ChangesSidebar.js
@@ -24,6 +24,17 @@
   }
 
   /**
+   * @param {!Workspace.UISourceCode} uiSourceCode
+   * @param {boolean=} omitFocus
+   */
+  selectUISourceCode(uiSourceCode, omitFocus) {
+    const treeElement = this._treeElements.get(uiSourceCode);
+    if (!treeElement)
+      return;
+    treeElement.select(omitFocus);
+  }
+
+  /**
    * @return {?Workspace.UISourceCode}
    */
   selectedUISourceCode() {
diff --git a/third_party/blink/renderer/devtools/front_end/changes/ChangesView.js b/third_party/blink/renderer/devtools/front_end/changes/ChangesView.js
index 1d97d1a..d9bf5cd 100644
--- a/third_party/blink/renderer/devtools/front_end/changes/ChangesView.js
+++ b/third_party/blink/renderer/devtools/front_end/changes/ChangesView.js
@@ -319,4 +319,24 @@
   Addition: 'addition',
   Equal: 'equal',
   Spacer: 'spacer'
-};
\ No newline at end of file
+};
+
+/**
+ * @implements {Common.Revealer}
+ */
+Changes.ChangesView.DiffUILocationRevealer = class {
+  /**
+   * @override
+   * @param {!Object} diffUILocation
+   * @param {boolean=} omitFocus
+   * @return {!Promise}
+   */
+  async reveal(diffUILocation, omitFocus) {
+    if (!(diffUILocation instanceof WorkspaceDiff.DiffUILocation))
+      throw new Error('Internal error: not a diff ui location');
+    /** @type {!Changes.ChangesView} */
+    const changesView = self.runtime.sharedInstance(Changes.ChangesView);
+    await UI.viewManager.showView('changes.changes');
+    changesView._changesSidebar.selectUISourceCode(diffUILocation.uiSourceCode, omitFocus);
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/changes/module.json b/third_party/blink/renderer/devtools/front_end/changes/module.json
index a1d831a..1875abc 100644
--- a/third_party/blink/renderer/devtools/front_end/changes/module.json
+++ b/third_party/blink/renderer/devtools/front_end/changes/module.json
@@ -7,6 +7,14 @@
             "title": "Changes",
             "persistence": "closeable",
             "className": "Changes.ChangesView"
+        },
+        {
+            "type": "@Common.Revealer",
+            "contextTypes": [
+                "WorkspaceDiff.DiffUILocation"
+            ],
+            "destination": "Changes drawer",
+            "className": "Changes.ChangesView.DiffUILocationRevealer"
         }
     ],
     "dependencies": [
diff --git a/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js b/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
index 509c25e..41902804 100644
--- a/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
+++ b/third_party/blink/renderer/devtools/front_end/console/ConsolePrompt.js
@@ -1,9 +1,7 @@
 // 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.
-/**
- * @unrestricted
- */
+
 Console.ConsolePrompt = class extends UI.Widget {
   constructor() {
     super();
@@ -11,6 +9,7 @@
     this._history = new Console.ConsoleHistoryManager();
 
     this._initialText = '';
+    /** @type {?UI.TextEditor} */
     this._editor = null;
     this._isBelowPromptEnabled = Runtime.experiments.isEnabled('consoleBelowPrompt');
     this._eagerPreviewElement = createElementWithClass('div', 'console-eager-preview');
@@ -25,6 +24,8 @@
     this._eagerPreviewElement.classList.toggle('hidden', !this._eagerEvalSetting.get());
 
     this.element.tabIndex = 0;
+    /** @type {?Promise} */
+    this._previewRequestForTest = null;
 
     self.runtime.extension(UI.TextEditorFactory).instance().then(gotFactory.bind(this));
 
@@ -39,6 +40,7 @@
       this._editor.configureAutocomplete({
         substituteRangeCallback: this._substituteRange.bind(this),
         suggestionsCallback: this._wordsWithQuery.bind(this),
+        tooltipCallback: (lineNumber, columnNumber) => this._tooltipCallback(lineNumber, columnNumber)
       });
       this._editor.widget().element.addEventListener('keydown', this._editorKeyDown.bind(this), true);
       this._editor.widget().show(this.element);
@@ -371,6 +373,35 @@
         .then(words => words.concat(historyWords));
   }
 
+  /**
+   * @param {number} lineNumber
+   * @param {number} columnNumber
+   * @return {!Promise<?Element>}
+   */
+  async _tooltipCallback(lineNumber, columnNumber) {
+    const before = this._editor.text(new TextUtils.TextRange(0, 0, lineNumber, columnNumber));
+    const result = await ObjectUI.javaScriptAutocomplete.argumentsHint(before);
+    if (!result)
+      return null;
+    const argumentsElement = createElement('span');
+    for (let i = 0; i < result.args.length; i++) {
+      if (i === result.argumentIndex || (i < result.argumentIndex && result.args[i].startsWith('...'))) {
+        const boldElement = createElement('b');
+        boldElement.textContent = result.args[i];
+        argumentsElement.appendChild(boldElement);
+      } else {
+        argumentsElement.createTextChild(result.args[i]);
+      }
+      if (i < result.args.length - 1)
+        argumentsElement.createTextChild(', ');
+    }
+    const tooltip = createElementWithClass('span', 'source-code');
+    tooltip.createTextChild('\u0192(');
+    tooltip.appendChild(argumentsElement);
+    tooltip.createTextChild(')');
+    return tooltip;
+  }
+
   _editorSetForTest() {
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/formatter/FormatterWorkerPool.js b/third_party/blink/renderer/devtools/front_end/formatter/FormatterWorkerPool.js
index 6890f50..95fe35d42 100644
--- a/third_party/blink/renderer/devtools/front_end/formatter/FormatterWorkerPool.js
+++ b/third_party/blink/renderer/devtools/front_end/formatter/FormatterWorkerPool.js
@@ -239,6 +239,23 @@
     return /** @type {!Promise<?{baseExpression: string, possibleSideEffects:boolean}>} */ (
         this._runTask('findLastExpression', {content}));
   }
+
+  /**
+   * @param {string} content
+   * @return {!Promise<?{baseExpression: string, possibleSideEffects:boolean, argumentIndex: number}>}
+   */
+  findLastFunctionCall(content) {
+    return /** @type {!Promise<?{baseExpression: string, possibleSideEffects:boolean, argumentIndex: number}>} */ (
+        this._runTask('findLastFunctionCall', {content}));
+  }
+
+  /**
+   * @param {string} content
+   * @return {!Promise<!Array<string>>}
+   */
+  argumentsList(content) {
+    return /** @type {!Promise<!Array<string>>} */ (this._runTask('argumentsList', {content}));
+  }
 };
 
 Formatter.FormatterWorkerPool.MaxWorkers = 2;
diff --git a/third_party/blink/renderer/devtools/front_end/formatter_worker/FormatterWorker.js b/third_party/blink/renderer/devtools/front_end/formatter_worker/FormatterWorker.js
index 791c5a81..d0d00c6 100644
--- a/third_party/blink/renderer/devtools/front_end/formatter_worker/FormatterWorker.js
+++ b/third_party/blink/renderer/devtools/front_end/formatter_worker/FormatterWorker.js
@@ -87,6 +87,12 @@
     case 'findLastExpression':
       postMessage(FormatterWorker.findLastExpression(params.content));
       break;
+    case 'findLastFunctionCall':
+      postMessage(FormatterWorker.findLastFunctionCall(params.content));
+      break;
+    case 'argumentsList':
+      postMessage(FormatterWorker.argumentsList(params.content));
+      break;
     default:
       console.error('Unsupport method name: ' + method);
   }
@@ -322,6 +328,95 @@
 
 /**
  * @param {string} content
+ * @return {?{baseExpression: string, possibleSideEffects:boolean, argumentIndex: number}}
+ */
+FormatterWorker.findLastFunctionCall = function(content) {
+  if (content.length > 10000)
+    return null;
+  try {
+    const tokenizer = acorn.tokenizer(content, {ecmaVersion: 9});
+    while (tokenizer.getToken().type !== acorn.tokTypes.eof) {
+    }
+  } catch (e) {
+    return null;
+  }
+
+  const suffix = '000)';
+  const base = FormatterWorker._lastCompleteExpression(content, suffix, new Set(['CallExpression', 'NewExpression']));
+  if (!base)
+    return null;
+  const callee = base.baseNode['callee'];
+  const argumentIndex = base.baseNode['arguments'].length - 1;
+  const baseExpression = base.baseExpression.substring(callee.start, callee.end);
+  const possibleSideEffects = FormatterWorker._nodeHasPossibleSideEffects(callee);
+  return {baseExpression, possibleSideEffects, argumentIndex};
+};
+
+/**
+ * @param {string} content
+ * @return {!Array<string>}
+ */
+FormatterWorker.argumentsList = function(content) {
+  if (content.length > 10000)
+    return [];
+  let parsed = null;
+  try {
+    // Try to parse as a function, anonymous function, or arrow function.
+    parsed = acorn.parse(`(${content})`, {ecmaVersion: 9});
+  } catch (e) {
+  }
+  if (!parsed) {
+    try {
+      // Try to parse as a method.
+      parsed = acorn.parse(`({${content}})`, {ecmaVersion: 9});
+    } catch (e) {
+    }
+  }
+  if (!parsed || !parsed.body || !parsed.body[0] || !parsed.body[0].expression)
+    return [];
+  const expression = parsed.body[0].expression;
+  let params = null;
+  switch (expression.type) {
+    case 'ClassExpression':
+      if (!expression.body.body)
+        break;
+      const constructor = expression.body.body.find(method => method.kind === 'constructor');
+      if (constructor)
+        params = constructor.value.params;
+      break;
+    case 'ObjectExpression':
+      if (!expression.properties[0] || !expression.properties[0].value)
+        break;
+      params = expression.properties[0].value.params;
+      break;
+    case 'FunctionExpression':
+    case 'ArrowFunctionExpression':
+      params = expression.params;
+      break;
+  }
+  if (!params)
+    return [];
+  return params.map(paramName);
+
+  function paramName(param) {
+    switch (param.type) {
+      case 'Identifier':
+        return param.name;
+      case 'AssignmentPattern':
+        return '?' + paramName(param.left);
+      case 'ObjectPattern':
+        return 'obj';
+      case 'ArrayPattern':
+        return 'arr';
+      case 'RestElement':
+        return '...' + paramName(param.argument);
+    }
+    return '?';
+  }
+};
+
+/**
+ * @param {string} content
  * @return {?{baseExpression: string, possibleSideEffects:boolean}}
  */
 FormatterWorker.findLastExpression = function(content) {
@@ -335,8 +430,6 @@
     return null;
   }
 
-  /** @type {!ESTree.Node} */
-  let ast;
   const suffix = '.DEVTOOLS';
   try {
     acorn.parse(content + suffix, {ecmaVersion: 9});
@@ -345,6 +438,23 @@
     if (parseError.message.startsWith('Unexpected token') && parseError.pos === content.length)
       return null;
   }
+  const base = FormatterWorker._lastCompleteExpression(content, suffix, new Set(['MemberExpression', 'Identifier']));
+  if (!base)
+    return null;
+  const {baseExpression, baseNode} = base;
+  const possibleSideEffects = FormatterWorker._nodeHasPossibleSideEffects(baseNode);
+  return {baseExpression, possibleSideEffects};
+};
+
+/**
+ * @param {string} content
+ * @param {string} suffix
+ * @param {!Set<string>} types
+ * @return {?{baseNode: !ESTree.Node, baseExpression: string}}
+ */
+FormatterWorker._lastCompleteExpression = function(content, suffix, types) {
+  /** @type {!ESTree.Node} */
+  let ast;
   let parsedContent = '';
   for (let i = 0; i < content.length; i++) {
     try {
@@ -357,7 +467,6 @@
   }
   if (!ast)
     return null;
-  const types = new Set(['MemberExpression', 'Identifier']);
   let baseNode = null;
   const walker = new FormatterWorker.ESTreeWalker(node => {
     if (baseNode || node.end < ast.end)
@@ -371,6 +480,14 @@
   let baseExpression = parsedContent.substring(baseNode.start, parsedContent.length - suffix.length);
   if (baseExpression.startsWith('{'))
     baseExpression = `(${baseExpression})`;
+  return {baseNode, baseExpression};
+};
+
+/**
+ * @param {!ESTree.Node} baseNode
+ * @return {boolean}
+ */
+FormatterWorker._nodeHasPossibleSideEffects = function(baseNode) {
   const sideEffectFreeTypes = new Set([
     'MemberExpression', 'Identifier', 'BinaryExpression', 'Literal', 'TemplateLiteral', 'TemplateElement',
     'ObjectExpression', 'ArrayExpression', 'Property', 'ThisExpression'
@@ -383,7 +500,7 @@
       return FormatterWorker.ESTreeWalker.SkipSubtree;
   });
   sideEffectwalker.walk(/** @type {!ESTree.Node} */ (baseNode));
-  return {baseExpression, possibleSideEffects};
+  return possibleSideEffects;
 };
 
 /**
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
index 57d4010..4811440 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
@@ -159,7 +159,7 @@
   }
 
   _createSearchPrompt() {
-    const text = this._searchHint.contentElement.createChild('div', 'search-button');
+    const text = this._searchHint.contentElement.createChild('div', 'search-suggestion');
     text.createChild('span').textContent = ls`Search headers and response bodies for `;
     const filterString = text.createChild('strong');
     const button = UI.createTextButton('Find All', () => this._openSearchView());
@@ -1486,6 +1486,7 @@
    * @param {!SDK.NetworkRequest} request
    */
   selectRequest(request) {
+    this.setTextFilterValue('');
     const node = this._reveal(request);
     if (node)
       node.select();
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkSearchScope.js b/third_party/blink/renderer/devtools/front_end/network/NetworkSearchScope.js
index de931827..8320051 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkSearchScope.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkSearchScope.js
@@ -59,16 +59,18 @@
     if (progress.isCanceled())
       return null;
     const locations = [];
+    if (stringMatchesQuery(request.url()))
+      locations.push(Network.UIRequestLocation.urlMatch(request));
     for (const header of request.requestHeaders()) {
       if (headerMatchesQuery(header))
-        locations.push(new Network.UIRequestLocation(request, header, null, null));
+        locations.push(Network.UIRequestLocation.requestHeaderMatch(request, header));
     }
     for (const header of request.responseHeaders) {
       if (headerMatchesQuery(header))
-        locations.push(new Network.UIRequestLocation(request, null, header, null));
+        locations.push(Network.UIRequestLocation.responseHeaderMatch(request, header));
     }
     for (const match of bodyMatches)
-      locations.push(new Network.UIRequestLocation(request, null, null, match));
+      locations.push(Network.UIRequestLocation.bodyMatch(request, match));
     progress.worked();
     return new Network.NetworkSearchResult(request, locations);
 
@@ -77,7 +79,14 @@
      * @return {boolean}
      */
     function headerMatchesQuery(header) {
-      const string = `${header.name}: ${header.value}`;
+      return stringMatchesQuery(`${header.name}: ${header.value}`);
+    }
+
+    /**
+     * @param {string} string
+     * @return {boolean}
+     */
+    function stringMatchesQuery(string) {
       const flags = searchConfig.ignoreCase() ? 'i' : '';
       const regExps = searchConfig.queries().map(query => new RegExp(query, flags));
       let pos = 0;
@@ -104,12 +113,45 @@
    * @param {?SDK.NetworkRequest.NameValue} requestHeader
    * @param {?SDK.NetworkRequest.NameValue} responseHeader
    * @param {?Common.ContentProvider.SearchMatch} searchMatch
+   * @param {boolean} urlMatch
    */
-  constructor(request, requestHeader, responseHeader, searchMatch) {
+  constructor(request, requestHeader, responseHeader, searchMatch, urlMatch) {
     this.request = request;
     this.requestHeader = requestHeader;
     this.responseHeader = responseHeader;
     this.searchMatch = searchMatch;
+    this.isUrlMatch = urlMatch;
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   * @param {?SDK.NetworkRequest.NameValue} header
+   */
+  static requestHeaderMatch(request, header) {
+    return new Network.UIRequestLocation(request, header, null, null, false);
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   * @param {?SDK.NetworkRequest.NameValue} header
+   */
+  static responseHeaderMatch(request, header) {
+    return new Network.UIRequestLocation(request, null, header, null, false);
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   * @param {?Common.ContentProvider.SearchMatch} searchMatch
+   */
+  static bodyMatch(request, searchMatch) {
+    return new Network.UIRequestLocation(request, null, null, searchMatch, false);
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   */
+  static urlMatch(request) {
+    return new Network.UIRequestLocation(request, null, null, null, true);
   }
 };
 
@@ -160,6 +202,8 @@
    */
   matchLineContent(index) {
     const location = this._locations[index];
+    if (location.isUrlMatch)
+      return this._request.url();
     const header = location.requestHeader || location.responseHeader;
     if (header)
       return header.value;
@@ -182,6 +226,8 @@
    */
   matchLabel(index) {
     const location = this._locations[index];
+    if (location.isUrlMatch)
+      return Common.UIString('URL');
     const header = location.requestHeader || location.responseHeader;
     if (header)
       return `${header.name}:`;
diff --git a/third_party/blink/renderer/devtools/front_end/network/networkPanel.css b/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
index ba6505e..1791144d 100644
--- a/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
+++ b/third_party/blink/renderer/devtools/front_end/network/networkPanel.css
@@ -137,6 +137,12 @@
     margin: 0 4px;
 }
 
+.open-search-view .search-suggestion {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
 .network-tabbed-pane {
     background-color: var(--toolbar-bg-color);
 }
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/JavaScriptAutocomplete.js b/third_party/blink/renderer/devtools/front_end/object_ui/JavaScriptAutocomplete.js
index 037dc20..6b8dfd9 100644
--- a/third_party/blink/renderer/devtools/front_end/object_ui/JavaScriptAutocomplete.js
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/JavaScriptAutocomplete.js
@@ -33,6 +33,43 @@
   }
 
   /**
+   * @param {string} fullText
+   * @return {!Promise<?{args: !Array<string>, argumentIndex: number}>}
+   */
+  async argumentsHint(fullText) {
+    const functionCall = await Formatter.formatterWorkerPool().findLastFunctionCall(fullText);
+    if (!functionCall)
+      return null;
+    const executionContext = UI.context.flavor(SDK.ExecutionContext);
+    if (!executionContext)
+      return null;
+    const result = await executionContext.evaluate(
+        {
+          expression: functionCall.baseExpression,
+          objectGroup: 'argumentsHint',
+          includeCommandLineAPI: true,
+          silent: true,
+          returnByValue: false,
+          generatePreview: false,
+          throwOnSideEffect: functionCall.possibleSideEffects,
+          timeout: functionCall.possibleSideEffects ? 500 : undefined
+        },
+        /* userGesture */ false, /* awaitPromise */ false);
+    if (!result || result.exceptionDetails || !result.object || result.object.type !== 'function')
+      return null;
+    executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
+
+    const description = result.object.description;
+    if (description.endsWith('{ [native code] }'))
+      return null;  // TODO(einbinder) support native function argument hints
+    const args = await Formatter.formatterWorkerPool().argumentsList(description);
+
+    if (!args.length)
+      return null;
+    return {args, argumentIndex: functionCall.argumentIndex};
+  }
+
+  /**
    * @param {string} text
    * @param {string} query
    * @return {!Promise<!UI.SuggestBox.Suggestions>}
diff --git a/third_party/blink/renderer/devtools/front_end/sources/GutterDiffPlugin.js b/third_party/blink/renderer/devtools/front_end/sources/GutterDiffPlugin.js
index 47286e8e..4ce4277 100644
--- a/third_party/blink/renderer/devtools/front_end/sources/GutterDiffPlugin.js
+++ b/third_party/blink/renderer/devtools/front_end/sources/GutterDiffPlugin.js
@@ -14,7 +14,7 @@
 
     /** @type {!Array<!Sources.GutterDiffPlugin.GutterDecoration>} */
     this._decorations = [];
-    this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, true);
+    this._textEditor.installGutter(Sources.GutterDiffPlugin.DiffGutterType, true);
     this._workspaceDiff = WorkspaceDiff.workspaceDiff();
     this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update, this);
     this._update();
@@ -97,6 +97,35 @@
 
   /**
    * @override
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {number} lineNumber
+   * @return {!Promise}
+   */
+  async populateLineGutterContextMenu(contextMenu, lineNumber) {
+    Sources.GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
+  }
+
+  /**
+   * @override
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {number} lineNumber
+   * @param {number} columnNumber
+   * @return {!Promise}
+   */
+  async populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) {
+    Sources.GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
+  }
+
+  static _appendRevealDiffContextMenu(contextMenu, uiSourceCode) {
+    if (!WorkspaceDiff.workspaceDiff().isUISourceCodeModified(uiSourceCode))
+      return;
+    contextMenu.revealSection().appendItem(ls`Local Modifications...`, () => {
+      Common.Revealer.reveal(new WorkspaceDiff.DiffUILocation(uiSourceCode));
+    });
+  }
+
+  /**
+   * @override
    */
   dispose() {
     for (const decoration of this._decorations)
@@ -140,7 +169,7 @@
       return;
     const element = createElementWithClass('div', 'diff-marker');
     element.textContent = '\u00A0';
-    this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, element);
+    this._textEditor.setGutterDecoration(location.lineNumber, Sources.GutterDiffPlugin.DiffGutterType, element);
     this._textEditor.toggleLineClass(location.lineNumber, this._className, true);
   }
 
@@ -148,10 +177,30 @@
     const location = this._position.resolve();
     if (!location)
       return;
-    this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, null);
+    this._textEditor.setGutterDecoration(location.lineNumber, Sources.GutterDiffPlugin.DiffGutterType, null);
     this._textEditor.toggleLineClass(location.lineNumber, this._className, false);
   }
 };
 
 /** @type {string} */
-SourceFrame.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff';
+Sources.GutterDiffPlugin.DiffGutterType = 'CodeMirror-gutter-diff';
+
+/**
+ * @implements {UI.ContextMenu.Provider}
+ * @unrestricted
+ */
+Sources.GutterDiffPlugin.ContextMenuProvider = class {
+  /**
+   * @override
+   * @param {!Event} event
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {!Object} target
+   */
+  appendApplicableItems(event, contextMenu, target) {
+    let uiSourceCode = /** @type {!Workspace.UISourceCode} */ (target);
+    const binding = Persistence.persistence.binding(uiSourceCode);
+    if (binding)
+      uiSourceCode = binding.network;
+    Sources.GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, uiSourceCode);
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/sources/module.json b/third_party/blink/renderer/devtools/front_end/sources/module.json
index bbb61d2..2097f804d 100644
--- a/third_party/blink/renderer/devtools/front_end/sources/module.json
+++ b/third_party/blink/renderer/devtools/front_end/sources/module.json
@@ -212,6 +212,13 @@
             "className": "Sources.WatchExpressionsSidebarPane"
         },
         {
+            "type": "@UI.ContextMenu.Provider",
+            "contextTypes": [
+                "Workspace.UISourceCode"
+            ],
+            "className": "Sources.GutterDiffPlugin.ContextMenuProvider"
+        },
+        {
             "type": "action",
             "actionId": "debugger.evaluate-selection",
             "className": "Sources.SourcesPanel.DebuggingActionDelegate",
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/TextEditorAutocompleteController.js b/third_party/blink/renderer/devtools/front_end/text_editor/TextEditorAutocompleteController.js
index 93d4e26..7a210d9 100644
--- a/third_party/blink/renderer/devtools/front_end/text_editor/TextEditorAutocompleteController.js
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/TextEditorAutocompleteController.js
@@ -22,7 +22,10 @@
     this._changes = this._changes.bind(this);
     this._blur = this._blur.bind(this);
     this._beforeChange = this._beforeChange.bind(this);
-    this._mouseDown = this.clearAutocomplete.bind(this);
+    this._mouseDown = () => {
+      this.clearAutocomplete();
+      this._tooltipGlassPane.hide();
+    };
     this._codeMirror.on('changes', this._changes);
     this._lastHintText = '';
     /** @type {?UI.SuggestBox} */
@@ -30,6 +33,14 @@
     /** @type {?string} */
     this._currentSuggestion = null;
     this._hintElement = createElementWithClass('span', 'auto-complete-text');
+
+    this._tooltipGlassPane = new UI.GlassPane();
+    this._tooltipGlassPane.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
+    this._tooltipGlassPane.setOutsideClickCallback(this._tooltipGlassPane.hide.bind(this._tooltipGlassPane));
+    this._tooltipElement = createElementWithClass('div', 'autocomplete-tooltip');
+    const shadowRoot =
+        UI.createShadowRootWithCoreStyles(this._tooltipGlassPane.contentElement, 'text_editor/autocompleteTooltip.css');
+    shadowRoot.appendChild(this._tooltipElement);
   }
 
   _initializeIfNeeded() {
@@ -220,14 +231,14 @@
   autocomplete(force) {
     this._initializeIfNeeded();
     if (this._codeMirror.somethingSelected()) {
-      this.clearAutocomplete();
+      this._hideSuggestBox();
       return;
     }
 
     const cursor = this._codeMirror.getCursor('head');
     const substituteRange = this._substituteRange(cursor.line, cursor.ch);
     if (!substituteRange || !this._validateSelectionsContexts(substituteRange)) {
-      this.clearAutocomplete();
+      this._hideSuggestBox();
       return;
     }
 
@@ -246,7 +257,7 @@
     function wordsAcquired(wordsWithQuery) {
       if (!wordsWithQuery.length || (wordsWithQuery.length === 1 && query === wordsWithQuery[0].text) ||
           (!this._suggestBox && hadSuggestBox)) {
-        this.clearAutocomplete();
+        this._hideSuggestBox();
         this._onSuggestionsShownForTest([]);
         return;
       }
@@ -259,6 +270,8 @@
           queryRange.startColumn !== oldQueryRange.startColumn)
         this._updateAnchorBox();
       this._suggestBox.updateSuggestions(this._anchorBox, wordsWithQuery, true, !this._isCursorAtEndOfLine(), query);
+      if (this._suggestBox.visible)
+        this._tooltipGlassPane.hide();
       this._onSuggestionsShownForTest(wordsWithQuery);
     }
   }
@@ -311,6 +324,11 @@
   }
 
   clearAutocomplete() {
+    this._tooltipGlassPane.hide();
+    this._hideSuggestBox();
+  }
+
+  _hideSuggestBox() {
     if (!this._suggestBox)
       return;
     this._suggestBox.hide();
@@ -328,6 +346,10 @@
    * @return {boolean}
    */
   keyDown(event) {
+    if (this._tooltipGlassPane.isShowing() && event.keyCode === UI.KeyboardShortcut.Keys.Esc.code) {
+      this._tooltipGlassPane.hide();
+      return true;
+    }
     if (!this._suggestBox)
       return false;
     switch (event.keyCode) {
@@ -417,6 +439,7 @@
   }
 
   _onScroll() {
+    this._tooltipGlassPane.hide();
     if (!this._suggestBox)
       return;
     const cursor = this._codeMirror.getCursor();
@@ -431,7 +454,34 @@
     }
   }
 
+  async _updateTooltip() {
+    const cursor = this._codeMirror.getCursor();
+    const tooltip = this._config.tooltipCallback ? await this._config.tooltipCallback(cursor.line, cursor.ch) : null;
+    const newCursor = this._codeMirror.getCursor();
+
+    if (newCursor.line !== cursor.line && newCursor.ch !== cursor.ch)
+      return;
+    if (this._suggestBox && this._suggestBox.visible)
+      return;
+
+    if (!tooltip) {
+      this._tooltipGlassPane.hide();
+      return;
+    }
+    const metrics = this._textEditor.cursorPositionToCoordinates(cursor.line, cursor.ch);
+    if (!metrics) {
+      this._tooltipGlassPane.hide();
+      return;
+    }
+
+    this._tooltipGlassPane.setContentAnchorBox(new AnchorBox(metrics.x, metrics.y, 0, metrics.height));
+    this._tooltipElement.removeChildren();
+    this._tooltipElement.appendChild(tooltip);
+    this._tooltipGlassPane.show(/** @type {!Document} */ (this._textEditor.element.ownerDocument));
+  }
+
   _onCursorActivity() {
+    this._updateTooltip();
     if (!this._suggestBox)
       return;
     const cursor = this._codeMirror.getCursor();
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/autocompleteTooltip.css b/third_party/blink/renderer/devtools/front_end/text_editor/autocompleteTooltip.css
new file mode 100644
index 0000000..f125260
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/autocompleteTooltip.css
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 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.
+ */
+.autocomplete-tooltip {
+  background-color: #FFFFFF;
+  pointer-events: none;
+  margin-left: -3px;
+  box-shadow: var(--drop-shadow);
+  overflow-x: hidden;
+  height: 20px;
+}
+
+.autocomplete-tooltip > * {
+  padding: 0px 4px;
+  white-space: nowrap;
+  vertical-align: middle;
+  line-height: 20px;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/module.json b/third_party/blink/renderer/devtools/front_end/text_editor/module.json
index 429098b..e1a6b60 100644
--- a/third_party/blink/renderer/devtools/front_end/text_editor/module.json
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/module.json
@@ -26,6 +26,7 @@
         "CodeMirrorTextEditor.js"
     ],
     "resources": [
+        "autocompleteTooltip.css",
         "cmdevtools.css"
     ],
     "skip_compilation": [
diff --git a/third_party/blink/renderer/devtools/front_end/ui/Fragment.js b/third_party/blink/renderer/devtools/front_end/ui/Fragment.js
index d30df1e..b7aabfb6 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/Fragment.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/Fragment.js
@@ -9,9 +9,6 @@
   constructor(element) {
     this._element = element;
 
-    /** @type {!Map<string, !Array<!UI.Fragment._State>>} */
-    this._states = new Map();
-
     /** @type {!Map<string, !Element>} */
     this._elementsById = new Map();
   }
@@ -32,45 +29,20 @@
   }
 
   /**
-   * @param {string} name
-   * @param {boolean} toggled
-   */
-  setState(name, toggled) {
-    const list = this._states.get(name);
-    if (list === undefined) {
-      console.error('Unknown state ' + name);
-      return;
-    }
-    for (const state of list) {
-      if (state.toggled === toggled)
-        continue;
-      state.toggled = toggled;
-      const value = state.attributeValue;
-      state.attributeValue = state.element.getAttribute(state.attributeName);
-      if (value === null)
-        state.element.removeAttribute(state.attributeName);
-      else
-        state.element.setAttribute(state.attributeName, value);
-    }
-  }
-
-  /**
    * @param {!Array<string>} strings
-   * @param {...*} vararg
+   * @param {...*} values
    * @return {!UI.Fragment}
    */
-  static build(strings, vararg) {
-    const values = Array.prototype.slice.call(arguments, 1);
+  static build(strings, ...values) {
     return UI.Fragment._render(UI.Fragment._template(strings), values);
   }
 
   /**
    * @param {!Array<string>} strings
-   * @param {...*} vararg
+   * @param {...*} values
    * @return {!UI.Fragment}
    */
-  static cached(strings, vararg) {
-    const values = Array.prototype.slice.call(arguments, 1);
+  static cached(strings, ...values) {
     let template = UI.Fragment._templateCache.get(strings);
     if (!template) {
       template = UI.Fragment._template(strings);
@@ -119,17 +91,7 @@
 
         const attributesToRemove = [];
         for (let i = 0; i < node.attributes.length; i++) {
-          let name = node.attributes[i].name;
-
-          if (name.startsWith('s-')) {
-            attributesToRemove.push(name);
-            name = name.substring(2);
-            const state = name.substring(0, name.indexOf('-'));
-            const attr = name.substring(state.length + 1);
-            nodesToMark.push(node);
-            binds.push({state: {name: state, attribute: attr, value: node.attributes[i].value}});
-            continue;
-          }
+          const name = node.attributes[i].name;
 
           if (!UI.Fragment._attributeMarkerRegex.test(name) &&
               !UI.Fragment._attributeMarkerRegex.test(node.attributes[i].value))
@@ -186,7 +148,6 @@
         /** @type {!Element} */ (content.firstChild === content.lastChild ? content.firstChild : content);
     const result = new UI.Fragment(resultElement);
 
-    const idByElement = new Map();
     const boundElements = [];
     for (let i = 0; i < template.binds.length; i++) {
       const className = UI.Fragment._class(i);
@@ -200,7 +161,6 @@
       const element = boundElements[bindIndex];
       if ('elementId' in bind) {
         result._elementsById.set(/** @type {string} */ (bind.elementId), element);
-        idByElement.set(element, bind.elementId);
       } else if ('replaceNodeIndex' in bind) {
         const value = values[/** @type {number} */ (bind.replaceNodeIndex)];
         let node = null;
@@ -211,13 +171,7 @@
         else
           node = createTextNode('' + value);
 
-        element.parentNode.insertBefore(node, element);
-        element.remove();
-      } else if ('state' in bind) {
-        const list = result._states.get(bind.state.name) || [];
-        list.push(
-            {attributeName: bind.state.attribute, attributeValue: bind.state.value, element: element, toggled: false});
-        result._states.set(bind.state.name, list);
+        element.parentNode.replaceChild(node, element);
       } else if ('attr' in bind) {
         if (bind.attr.names.length === 2 && bind.attr.values.length === 1 &&
             typeof values[bind.attr.index] === 'function') {
@@ -238,31 +192,9 @@
           }
         }
       } else {
-        throw 'Unexpected bind';
+        throw new Error('Unexpected bind');
       }
     }
-
-    // We do this after binds so that querySelector works.
-    const shadows = result._element.querySelectorAll('x-shadow');
-    for (const shadow of shadows) {
-      if (!shadow.parentElement)
-        throw 'There must be a parent element here';
-      const shadowRoot = UI.createShadowRootWithCoreStyles(shadow.parentElement);
-      if (shadow.parentElement.tagName === 'X-WIDGET')
-        shadow.parentElement._shadowRoot = shadowRoot;
-      const children = [];
-      while (shadow.lastChild) {
-        children.push(shadow.lastChild);
-        shadow.lastChild.remove();
-      }
-      for (let i = children.length - 1; i >= 0; i--)
-        shadowRoot.appendChild(children[i]);
-      const id = idByElement.get(shadow);
-      if (id)
-        result._elementsById.set(id, /** @type {!Element} */ (/** @type {!Node} */ (shadowRoot)));
-      shadow.remove();
-    }
-
     return result;
   }
 };
@@ -277,24 +209,8 @@
 
 /**
  * @typedef {!{
- *   attributeName: string,
- *   attributeValue: string,
- *   element: !Element,
- *   toggled: boolean
- * }}
- */
-UI.Fragment._State;
-
-/**
- * @typedef {!{
  *   elementId: (string|undefined),
  *
- *   state: (!{
- *     name: string,
- *     attribute: string,
- *     value: string
- *   }|undefined),
- *
  *   attr: (!{
  *     index: number,
  *     names: !Array<string>,
@@ -315,3 +231,12 @@
 UI.Fragment._class = index => 'template-class-' + index;
 
 UI.Fragment._templateCache = new Map();
+
+/**
+ * @param {!Array<string>} strings
+ * @param {...*} vararg
+ * @return {!Element}
+ */
+UI.html = (strings, ...vararg) => {
+  return UI.Fragment.cached(strings, ...vararg).element();
+};
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js b/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
index 8bf364db..d8aeb04 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/TextEditor.js
@@ -116,6 +116,7 @@
 /**
  * @typedef {{
  *     substituteRangeCallback: ((function(number, number):?TextUtils.TextRange)|undefined),
+ *     tooltipCallback: ((function(number, number):?Element)|undefined),
  *     suggestionsCallback: ((function(!TextUtils.TextRange, !TextUtils.TextRange, boolean=):?Promise.<!UI.SuggestBox.Suggestions>)|undefined),
  *     isWordChar: ((function(string):boolean)|undefined)
  * }}
diff --git a/third_party/blink/renderer/devtools/front_end/ui/XLink.js b/third_party/blink/renderer/devtools/front_end/ui/XLink.js
index e7bdbbc..a29a72af 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/XLink.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/XLink.js
@@ -19,9 +19,9 @@
     className = className || '';
     // clang-format off
     // TODO(dgozman): migrate css from 'devtools-link' to 'x-link'.
-    return (UI.Fragment.cached`
+    return UI.html`
         <x-link href='${url}' class='${className} devtools-link' ${preventClick ? 'no-click' : ''}
-        >${linkText.trimMiddle(UI.MaxLengthForDisplayedURLs)}</x-link>`).element();
+        >${linkText.trimMiddle(UI.MaxLengthForDisplayedURLs)}</x-link>`;
     // clang-format on
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/workspace_diff/WorkspaceDiff.js b/third_party/blink/renderer/devtools/front_end/workspace_diff/WorkspaceDiff.js
index 98791f0c..4e05823 100644
--- a/third_party/blink/renderer/devtools/front_end/workspace_diff/WorkspaceDiff.js
+++ b/third_party/blink/renderer/devtools/front_end/workspace_diff/WorkspaceDiff.js
@@ -60,6 +60,14 @@
 
   /**
    * @param {!Workspace.UISourceCode} uiSourceCode
+   * @return {boolean}
+   */
+  isUISourceCodeModified(uiSourceCode) {
+    return this._modifiedUISourceCodes.has(uiSourceCode) || this._loadingUISourceCodes.has(uiSourceCode);
+  }
+
+  /**
+   * @param {!Workspace.UISourceCode} uiSourceCode
    * @return {!WorkspaceDiff.WorkspaceDiff.UISourceCodeDiff}
    */
   _uiSourceCodeDiff(uiSourceCode) {
@@ -303,4 +311,13 @@
   return WorkspaceDiff.WorkspaceDiff._instance;
 };
 
+WorkspaceDiff.DiffUILocation = class {
+  /**
+   * @param {!Workspace.UISourceCode} uiSourceCode
+   */
+  constructor(uiSourceCode) {
+    this.uiSourceCode = uiSourceCode;
+  }
+};
+
 WorkspaceDiff.WorkspaceDiff.UpdateTimeout = 200;
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/hashes b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/hashes
index 196e0a51a..3f1964e 100644
--- a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/hashes
+++ b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/hashes
@@ -1,2 +1,2 @@
-41de8b8314e09505c2d54694c45e76cbcaba3a7a55aa84bad7be64f79d29ce36 jsdoc_validator.jar
-a85a8bc9d70c97eeb9a0c6bbc08687595664941cd54ab3a9dda2ea61691404c0 src
+e9e1827178cd3cbef994fca9d1abdb91157ea3ee3f8c1484bfe6611b106df24d jsdoc_validator.jar
+7dc86cebcd4d0e7251458410fdcfeea8c26ed2f00c0a06ee4484e4dd37f67e89 src
diff --git a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/jsdoc_validator.jar b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/jsdoc_validator.jar
index 994218ca..67671149 100644
--- a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/jsdoc_validator.jar
+++ b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/jsdoc_validator.jar
Binary files differ
diff --git a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
index 0e42182c..49f01c3 100644
--- a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
+++ b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/src/org/chromium/devtools/jsdoc/checks/ContextTrackingValidationCheck.java
@@ -132,6 +132,9 @@
             Node paramNode = parametersNode.getChildAtIndex(i);
             String paramText = state.getContext().getNodeText(paramNode);
 
+            // Handle rest parameters
+            if ("...".equals(paramText)) continue;
+
             // Handle default parameters (ES6)
             String paramName = paramText.split("=")[0].trim();
             parameterNames.add(paramName);
diff --git a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/tests/function.js b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/tests/function.js
index 1343675..57f27604 100644
--- a/third_party/blink/renderer/devtools/scripts/jsdoc_validator/tests/function.js
+++ b/third_party/blink/renderer/devtools/scripts/jsdoc_validator/tests/function.js
@@ -378,3 +378,9 @@
      */
     returnNumber: function() { }
 }
+
+/**
+ * @param {string} a
+ * @param {...number} abc
+ */
+function funcParamsOK3(a, ...abc) {}
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 0f62cf5..c145ca5 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -108,7 +108,7 @@
       modification_count_(0),
       relation_cache_(new AXRelationCache(this)),
       notification_post_timer_(
-          document.GetTaskRunner(TaskType::kInternalAccessibility),
+          document.GetTaskRunner(TaskType::kInternalDefault),
           this,
           &AXObjectCacheImpl::NotificationPostTimerFired),
       accessibility_event_permission_(mojom::PermissionStatus::ASK),
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.cc b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.cc
index ca66a803..af62f2d 100644
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.cc
@@ -44,8 +44,8 @@
   return image_layer_bridge_->GetImage();
 }
 
-void ImageBitmapRenderingContextBase::SetUV(const FloatPoint left_top,
-                                            const FloatPoint right_bottom) {
+void ImageBitmapRenderingContextBase::SetUV(const FloatPoint& left_top,
+                                            const FloatPoint& right_bottom) {
   image_layer_bridge_->SetUV(left_top, right_bottom);
 }
 
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
index 9935c55e..9e8a823 100644
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
@@ -34,7 +34,7 @@
   bool isContextLost() const override { return false; }
   void SetImage(ImageBitmap*);
   scoped_refptr<StaticBitmapImage> GetImage(AccelerationHint) const final;
-  void SetUV(const FloatPoint left_top, const FloatPoint right_bottom);
+  void SetUV(const FloatPoint& left_top, const FloatPoint& right_bottom);
   bool IsComposited() const final { return true; }
   bool IsAccelerated() const final;
 
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
index f60a23a..bf2a00d 100644
--- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -38,8 +38,16 @@
 #include "third_party/blink/renderer/modules/gamepad/gamepad_list.h"
 #include "third_party/blink/renderer/modules/vr/navigator_vr.h"
 
+namespace blink {
+
 namespace {
 
+// A button press must have a value at least this large to qualify as a user
+// activation. The selected value should be greater than 0.5 so that axes
+// incorrectly mapped as triggers do not generate activations in the idle
+// position.
+const double kButtonActivationThreshold = 0.9;
+
 void HasGamepadConnectionChanged(const String& old_id,
                                  const String& new_id,
                                  bool old_connected,
@@ -55,9 +63,25 @@
     *gamepad_lost = id_changed || (old_connected && !new_connected);
 }
 
-}  // namespace
+bool HasUserActivation(GamepadList& gamepads) {
+  // A button press counts as a user activation if the button's value is greater
+  // than the activation threshold. A threshold is used so that analog buttons
+  // or triggers do not generate an activation from a light touch.
+  for (size_t pad_index = 0; pad_index < gamepads.length(); ++pad_index) {
+    Gamepad* pad = gamepads.item(pad_index);
+    if (pad) {
+      const GamepadButtonVector& buttons = pad->buttons();
+      for (size_t i = 0; i < buttons.size(); ++i) {
+        double value = buttons.at(i)->value();
+        if (value > kButtonActivationThreshold)
+          return true;
+      }
+    }
+  }
+  return false;
+}
 
-namespace blink {
+}  // namespace
 
 template <typename T>
 static void SampleGamepad(unsigned index,
@@ -152,6 +176,7 @@
   return *supplement;
 }
 
+// static
 GamepadList* NavigatorGamepad::getGamepads(Navigator& navigator) {
   return NavigatorGamepad::From(navigator).Gamepads();
 }
@@ -167,6 +192,13 @@
   }
 
   SampleAndCheckConnectedGamepads();
+
+  // Allow gamepad button presses to qualify as user activations.
+  if (RuntimeEnabledFeatures::UserActivationV2Enabled() &&
+      GetPage()->IsPageVisible() && HasUserActivation(*gamepads_)) {
+    Frame::NotifyUserActivation(GetFrame(), UserGestureToken::kNewGesture);
+  }
+
   return gamepads_.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index cfeac8fc..c144dcf 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -49,6 +49,30 @@
   Member<ScriptPromiseResolver> resolver_;
 };
 
+class SetParametersRequest : public RTCVoidRequestScriptPromiseResolverImpl {
+ public:
+  SetParametersRequest(ScriptPromiseResolver* resolver, RTCRtpSender* sender)
+      : RTCVoidRequestScriptPromiseResolverImpl(resolver), sender_(sender) {}
+
+  void RequestSucceeded() override {
+    sender_->ClearLastReturnedParameters();
+    RTCVoidRequestScriptPromiseResolverImpl::RequestSucceeded();
+  }
+
+  void RequestFailed(const webrtc::RTCError& error) override {
+    sender_->ClearLastReturnedParameters();
+    RTCVoidRequestScriptPromiseResolverImpl::RequestFailed(error);
+  }
+
+  void Trace(blink::Visitor* visitor) override {
+    visitor->Trace(sender_);
+    RTCVoidRequestScriptPromiseResolverImpl::Trace(visitor);
+  }
+
+ private:
+  Member<RTCRtpSender> sender_;
+};
+
 bool HasInvalidModification(const RTCRtpParameters& parameters,
                             const RTCRtpParameters& new_parameters) {
   if (parameters.hasTransactionId() != new_parameters.hasTransactionId() ||
@@ -296,10 +320,10 @@
         "getParameters() needs to be called before setParameters()."));
     return promise;
   }
-  // The specification mentions that some fields in the dictionnary should not
+  // The specification mentions that some fields in the dictionary should not
   // be modified. Some of those checks are done in the lower WebRTC layer, but
   // there is no perfect 1-1 mapping between the Javascript layer and native.
-  // So we save the last returned dictionnary and enforce the check at this
+  // So we save the last returned dictionary and enforce the check at this
   // level instead.
   if (HasInvalidModification(last_returned_parameters_.value(), parameters)) {
     resolver->Reject(
@@ -321,12 +345,16 @@
     degradation_preference = blink::WebRTCDegradationPreference::Balanced;
   }
 
-  auto* request = RTCVoidRequestScriptPromiseResolverImpl::Create(resolver);
+  auto* request = new SetParametersRequest(resolver, this);
   sender_->SetParameters(std::move(encodings), degradation_preference.value(),
                          request);
   return promise;
 }
 
+void RTCRtpSender::ClearLastReturnedParameters() {
+  last_returned_parameters_.reset();
+}
+
 ScriptPromise RTCRtpSender::getStats(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index 22f0b982..9f655eaf 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -45,6 +45,7 @@
   // Sets the track. This must be called when the |WebRTCRtpSender| has its
   // track updated, and the |track| must match the |WebRTCRtpSender::Track|.
   void SetTrack(MediaStreamTrack*);
+  void ClearLastReturnedParameters();
   MediaStreamVector streams() const;
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h b/third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h
index cb46d3d..eed62be 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_void_request_script_promise_resolver_impl.h
@@ -12,7 +12,7 @@
 
 class ScriptPromiseResolver;
 
-class RTCVoidRequestScriptPromiseResolverImpl final : public RTCVoidRequest {
+class RTCVoidRequestScriptPromiseResolverImpl : public RTCVoidRequest {
  public:
   static RTCVoidRequestScriptPromiseResolverImpl* Create(
       ScriptPromiseResolver*);
@@ -24,7 +24,7 @@
 
   void Trace(blink::Visitor*) override;
 
- private:
+ protected:
   RTCVoidRequestScriptPromiseResolverImpl(ScriptPromiseResolver*);
 
   Member<ScriptPromiseResolver> resolver_;
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index f04f1d7..6212d2c 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -343,10 +343,12 @@
   switch (state_) {
     case WebRemotePlaybackState::kConnecting:
       DispatchEvent(Event::Create(EventTypeNames::connecting));
-      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled() &&
-          media_element_->IsHTMLVideoElement()) {
-        // TODO(xjz): Pass the remote device name.
-        ToHTMLVideoElement(media_element_)->MediaRemotingStarted(WebString());
+      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
+        if (media_element_->IsHTMLVideoElement()) {
+          // TODO(xjz): Pass the remote device name.
+          ToHTMLVideoElement(media_element_)->MediaRemotingStarted(WebString());
+        }
+        media_element_->FlingingStarted();
       }
       break;
     case WebRemotePlaybackState::kConnected:
@@ -354,11 +356,14 @@
       break;
     case WebRemotePlaybackState::kDisconnected:
       DispatchEvent(Event::Create(EventTypeNames::disconnect));
-      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled() &&
-          media_element_->IsHTMLVideoElement()) {
-        ToHTMLVideoElement(media_element_)
-            ->MediaRemotingStopped(
-                WebLocalizedString::kMediaRemotingStopNoText);
+      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
+        if (media_element_->IsHTMLVideoElement()) {
+          ToHTMLVideoElement(media_element_)
+              ->MediaRemotingStopped(
+                  WebLocalizedString::kMediaRemotingStopNoText);
+        }
+        presentation_id_ = "";
+        media_element_->FlingingStopped();
       }
       break;
   }
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.cc b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
index 3f147cb..ff4dcf5 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
@@ -586,12 +586,10 @@
   return state_;
 }
 
-unsigned DOMWebSocket::bufferedAmount() const {
-  uint64_t sum = buffered_amount_after_close_ + buffered_amount_;
-
-  if (sum > std::numeric_limits<unsigned>::max())
-    return std::numeric_limits<unsigned>::max();
-  return sum;
+uint64_t DOMWebSocket::bufferedAmount() const {
+  // TODO(ricea): Check for overflow once machines with exabytes of RAM become
+  // commonplace.
+  return buffered_amount_after_close_ + buffered_amount_;
 }
 
 String DOMWebSocket::protocol() const {
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.h b/third_party/blink/renderer/modules/websockets/dom_websocket.h
index 01e246e..8a29012 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.h
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.h
@@ -103,7 +103,7 @@
 
   const KURL& url() const;
   State readyState() const;
-  unsigned bufferedAmount() const;
+  uint64_t bufferedAmount() const;
 
   String protocol() const;
   String extensions() const;
diff --git a/third_party/blink/renderer/modules/websockets/websocket.idl b/third_party/blink/renderer/modules/websockets/websocket.idl
index 8d23f66..d979eb17 100644
--- a/third_party/blink/renderer/modules/websockets/websocket.idl
+++ b/third_party/blink/renderer/modules/websockets/websocket.idl
@@ -49,7 +49,7 @@
     const unsigned short CLOSING = 2;
     const unsigned short CLOSED = 3;
     readonly attribute unsigned short readyState;
-    readonly attribute unsigned long bufferedAmount;
+    readonly attribute unsigned long long bufferedAmount;
 
     // networking
     attribute EventHandler onopen;
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 7071d13..6cd2d22 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -320,9 +320,9 @@
   // execution context caused extreme input delay due to processing
   // multiple frames without yielding, see crbug.com/701444.
   Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
-      FROM_HERE, WTF::Bind(&XRFrameProvider::ProcessScheduledFrame,
-                           WrapWeakPersistent(this), base::nullopt,
-                           time_delta.InSecondsF()));
+      FROM_HERE,
+      WTF::Bind(&XRFrameProvider::ProcessScheduledFrame,
+                WrapWeakPersistent(this), nullptr, time_delta.InSecondsF()));
 }
 
 void XRFrameProvider::OnNonExclusiveVSync(double timestamp) {
@@ -337,7 +337,7 @@
 
   Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
       FROM_HERE, WTF::Bind(&XRFrameProvider::ProcessScheduledFrame,
-                           WrapWeakPersistent(this), base::nullopt, timestamp));
+                           WrapWeakPersistent(this), nullptr, timestamp));
 }
 
 void XRFrameProvider::OnNonExclusivePose(device::mojom::blink::VRPosePtr pose) {
@@ -396,7 +396,7 @@
 }
 
 void XRFrameProvider::ProcessScheduledFrame(
-    base::Optional<device::mojom::blink::VRMagicWindowFrameDataPtr> frame_data,
+    device::mojom::blink::VRMagicWindowFrameDataPtr frame_data,
     double timestamp) {
   DVLOG(2) << __FUNCTION__;
 
@@ -441,7 +441,7 @@
       if (frame_data) {
         // TODO(https://crbug.com/837883): only render background for
         // sessions that are using AR.
-        RenderBackgroundImage(frame_data.value(), session);
+        RenderBackgroundImage(frame_data, session);
       }
 
       if (frame_pose_ && frame_pose_->input_state.has_value()) {
@@ -450,8 +450,7 @@
       }
 
       if (frame_data) {
-        session->SetNonExclusiveProjectionMatrix(
-            frame_data.value()->projection_matrix);
+        session->SetNonExclusiveProjectionMatrix(frame_data->projection_matrix);
       }
 
       std::unique_ptr<TransformationMatrix> pose_matrix =
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
index b1e1402..0109a2a3 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -62,8 +62,7 @@
       device::mojom::blink::VRDisplayFrameTransportOptionsPtr);
   void OnPresentationProviderConnectionError();
   void ProcessScheduledFrame(
-      base::Optional<device::mojom::blink::VRMagicWindowFrameDataPtr>
-          frame_data,
+      device::mojom::blink::VRMagicWindowFrameDataPtr frame_data,
       double timestamp);
 
   void RenderBackgroundImage(
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index f00fcccd..bf9ad5b 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1357,6 +1357,8 @@
     "text/bidi_run_list.h",
     "text/bidi_text_run.cc",
     "text/bidi_text_run.h",
+    "text/capitalize.cc",
+    "text/capitalize.h",
     "text/character.cc",
     "text/character.h",
     "text/character_emoji.cc",
@@ -1889,6 +1891,7 @@
     "testing/tree_test_helpers.h",
     "text/bidi_resolver_test.cc",
     "text/bidi_test_harness.h",
+    "text/capitalize_test.cc",
     "text/character_test.cc",
     "text/date_time_format_test.cc",
     "text/hyphenation_test.cc",
diff --git a/third_party/blink/renderer/platform/feature_policy/feature_policy.cc b/third_party/blink/renderer/platform/feature_policy/feature_policy.cc
index 3f055bd4..6ae7b0b 100644
--- a/third_party/blink/renderer/platform/feature_policy/feature_policy.cc
+++ b/third_party/blink/renderer/platform/feature_policy/feature_policy.cc
@@ -152,6 +152,8 @@
       return true;
     case mojom::FeaturePolicyFeature::kUnsizedMedia:
     case mojom::FeaturePolicyFeature::kVerticalScroll:
+    case mojom::FeaturePolicyFeature::kLegacyImageFormats:
+    case mojom::FeaturePolicyFeature::kImageCompression:
       return RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled();
     default:
       return false;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index 93cdc43..4dff05f 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -30,10 +30,12 @@
 
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "cc/layers/texture_layer.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_layer.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_metrics.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
@@ -98,7 +100,11 @@
 Canvas2DLayerBridge::~Canvas2DLayerBridge() {
   BeginDestruction();
   DCHECK(destruction_in_progress_);
-  layer_.reset();
+  if (layer_) {
+    web_layer_ = nullptr;
+    layer_->ClearClient();
+    layer_ = nullptr;
+  }
 }
 
 void Canvas2DLayerBridge::StartRecording() {
@@ -298,13 +304,15 @@
   }
 
   if (resource_provider_ && resource_provider_->IsAccelerated() && !layer_) {
-    layer_ =
-        Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
-            this);
-    layer_->SetOpaque(ColorParams().GetOpacityMode() == kOpaque);
+    layer_ = cc::TextureLayer::CreateForMailbox(this);
+    layer_->SetIsDrawable(true);
+    layer_->SetContentsOpaque(ColorParams().GetOpacityMode() == kOpaque);
     layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque);
-    GraphicsLayer::RegisterContentsLayer(layer_->Layer());
     layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+    web_layer_ =
+        Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+            layer_.get());
+    GraphicsLayer::RegisterContentsLayer(web_layer_.get());
   }
 
   if (resource_provider_ && IsHibernating()) {
@@ -396,12 +404,12 @@
   ResetResourceProvider();
 
   if (layer_ && acceleration_mode_ != kDisableAcceleration) {
-    GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+    GraphicsLayer::UnregisterContentsLayer(web_layer_.get());
     layer_->ClearTexture();
     // Orphaning the layer is required to trigger the recration of a new layer
     // in the case where destruction is caused by a canvas resize. Test:
     // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
-    layer_->Layer()->RemoveFromParent();
+    layer_->RemoveFromParent();
   }
 
   DCHECK(!bytes_allocated_);
@@ -654,7 +662,7 @@
   DCHECK(!destruction_in_progress_);
   // Trigger lazy layer creation
   GetOrCreateResourceProvider(kPreferAcceleration);
-  return layer_ ? layer_->Layer() : nullptr;
+  return web_layer_.get();
 }
 
 void Canvas2DLayerBridge::DidDraw(const FloatRect& rect) {
@@ -712,7 +720,7 @@
 void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
   DCHECK(!destruction_in_progress_);
   if (layer_ && acceleration_mode_ != kDisableAcceleration)
-    layer_->Layer()->InvalidateRect(EnclosingIntRect(dirty_rect));
+    layer_->SetNeedsDisplayRect(EnclosingIntRect(dirty_rect));
 }
 
 scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot(
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
index 7be67ddd..6fd73e7 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -32,7 +32,7 @@
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "cc/layers/texture_layer_client.h"
-#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
@@ -50,12 +50,17 @@
 
 struct SkImageInfo;
 
+namespace cc {
+class TextureLayer;
+}
+
 namespace blink {
 
 class Canvas2DLayerBridgeTest;
 class CanvasResourceProvider;
 class SharedContextRateLimiter;
 class StaticBitmapImage;
+class WebLayer;
 
 #if defined(OS_MACOSX)
 // Canvas hibernation is currently disabled on MacOS X due to a bug that causes
@@ -181,7 +186,8 @@
   std::unique_ptr<CanvasResourceProvider> resource_provider_;
   std::unique_ptr<PaintRecorder> recorder_;
   sk_sp<SkImage> hibernation_image_;
-  std::unique_ptr<WebExternalTextureLayer> layer_;
+  std::unique_ptr<WebLayer> web_layer_;  // Wrapper for |layer_|.
+  scoped_refptr<cc::TextureLayer> layer_;
   std::unique_ptr<SharedContextRateLimiter> rate_limiter_;
   std::unique_ptr<Logger> logger_;
   int msaa_sample_count_;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 9bcffdae..ce5db67 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -11,7 +11,6 @@
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
-#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -49,13 +48,11 @@
   GLuint GetBackingTextureHandleForOverwrite() override {
     GrBackendTexture backend_texture = GetSkSurface()->getBackendTexture(
         SkSurface::kDiscardWrite_TextureHandleAccess);
-    if (!backend_texture.isValid()) {
+    if (!backend_texture.isValid())
       return 0;
-    }
     GrGLTextureInfo info;
-    if (!backend_texture.getGLTextureInfo(&info)) {
+    if (!backend_texture.getGLTextureInfo(&info))
       return 0;
-    }
     return info.fID;
   }
 
@@ -115,7 +112,7 @@
                                        ColorParams().GetSkSurfaceProps());
   }
 
-  unsigned msaa_sample_count_;
+  const unsigned msaa_sample_count_;
 };
 
 // CanvasResourceProviderTextureGpuMemoryBuffer
@@ -141,7 +138,7 @@
 
   ~CanvasResourceProviderTextureGpuMemoryBuffer() override = default;
 
- protected:
+ private:
   scoped_refptr<CanvasResource> CreateResource() final {
     return CanvasResourceGpuMemoryBuffer::Create(
         Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
@@ -179,6 +176,35 @@
 
     return output_resource;
   }
+
+  void RecycleResource(scoped_refptr<CanvasResource> resource) override {
+    DCHECK(resource->HasOneRef());
+    if (resource_recycling_enabled_)
+      recycled_resources_.push_back(std::move(resource));
+  }
+
+  void SetResourceRecyclingEnabled(bool value) override {
+    resource_recycling_enabled_ = value;
+    if (!resource_recycling_enabled_)
+      ClearRecycledResources();
+  }
+
+  void ClearRecycledResources() { recycled_resources_.clear(); }
+
+  scoped_refptr<CanvasResource> NewOrRecycledResource() {
+    if (recycled_resources_.size()) {
+      scoped_refptr<CanvasResource> resource =
+          std::move(recycled_resources_.back());
+      recycled_resources_.pop_back();
+      // Recycling implies releasing the old content
+      resource->WaitSyncTokenBeforeRelease();
+      return resource;
+    }
+    return CreateResource();
+  }
+
+  WTF::Vector<scoped_refptr<CanvasResource>> recycled_resources_;
+  bool resource_recycling_enabled_ = true;
 };
 
 // CanvasResourceProviderBitmap
@@ -212,8 +238,6 @@
         kPremul_SkAlphaType, ColorParams().GetSkColorSpaceForSkSurfaces());
     return SkSurface::MakeRaster(info, ColorParams().GetSkSurfaceProps());
   }
-
-  sk_sp<SkSurface> surface_;
 };
 
 // CanvasResourceProvider base class implementation
@@ -374,9 +398,8 @@
 }
 
 SkSurface* CanvasResourceProvider::GetSkSurface() const {
-  if (!surface_) {
+  if (!surface_)
     surface_ = CreateSkSurface();
-  }
   return surface_.get();
 }
 
@@ -470,29 +493,9 @@
   GetSkSurface()->flush();
 }
 
-void CanvasResourceProvider::RecycleResource(
-    scoped_refptr<CanvasResource> resource) {
-  DCHECK(resource->HasOneRef());
-  if (resource_recycling_enabled_)
-    recycled_resources_.push_back(std::move(resource));
-}
-
-void CanvasResourceProvider::SetResourceRecyclingEnabled(bool value) {
-  resource_recycling_enabled_ = value;
-  if (!resource_recycling_enabled_)
-    ClearRecycledResources();
-}
-
-scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
-  if (recycled_resources_.size()) {
-    scoped_refptr<CanvasResource> resource =
-        std::move(recycled_resources_.back());
-    recycled_resources_.pop_back();
-    // Recycling implies releasing the old content
-    resource->WaitSyncTokenBeforeRelease();
-    return resource;
-  }
-  return CreateResource();
+void CanvasResourceProvider::RecycleResource(scoped_refptr<CanvasResource>) {
+  // To be implemented in subclasses that use resource recycling.
+  NOTREACHED();
 }
 
 bool CanvasResourceProvider::IsGpuContextLost() const {
@@ -515,15 +518,10 @@
   // if this wasn't required, but the canvas is currently filled with the magic
   // transparency color. Can we have another way to manage this?
   DCHECK(IsValid());
-  if (color_params_.GetOpacityMode() == kOpaque) {
+  if (color_params_.GetOpacityMode() == kOpaque)
     Canvas()->clear(SK_ColorBLACK);
-  } else {
+  else
     Canvas()->clear(SK_ColorTRANSPARENT);
-  }
-}
-
-void CanvasResourceProvider::ClearRecycledResources() {
-  recycled_resources_.clear();
 }
 
 void CanvasResourceProvider::InvalidateSurface() {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index 40165aa..c68faf8 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -12,6 +12,7 @@
 #include "cc/raster/playback_image_provider.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
@@ -37,7 +38,6 @@
 
 namespace blink {
 
-class CanvasResource;
 class StaticBitmapImage;
 class WebGraphicsContext3DProviderWrapper;
 
@@ -93,9 +93,10 @@
   virtual bool IsValid() const = 0;
   virtual bool IsAccelerated() const = 0;
   uint32_t ContentUniqueID() const;
-  void ClearRecycledResources();
-  void RecycleResource(scoped_refptr<CanvasResource>);
-  void SetResourceRecyclingEnabled(bool);
+
+  virtual void RecycleResource(scoped_refptr<CanvasResource>);
+  virtual void SetResourceRecyclingEnabled(bool) {}
+
   SkSurface* GetSkSurface() const;
   bool IsGpuContextLost() const;
   bool WritePixels(const SkImageInfo& orig_info,
@@ -116,7 +117,6 @@
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() {
     return context_provider_wrapper_;
   }
-  scoped_refptr<CanvasResource> NewOrRecycledResource();
   SkFilterQuality FilterQuality() const { return filter_quality_; }
   base::WeakPtr<CanvasResourceProvider> CreateWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
@@ -160,9 +160,7 @@
   std::unique_ptr<cc::SkiaPaintCanvas> canvas_;
   mutable sk_sp<SkSurface> surface_;  // mutable for lazy init
   std::unique_ptr<SkCanvas> xform_canvas_;
-  WTF::Vector<scoped_refptr<CanvasResource>> recycled_resources_;
   SkFilterQuality filter_quality_;
-  bool resource_recycling_enabled_ = true;
 
   const cc::PaintImage::Id snapshot_paint_image_id_;
   cc::PaintImage::ContentId snapshot_paint_image_content_id_ =
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 3a48462b..a6c07bb 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -53,6 +53,12 @@
 }
 
 PaintArtifactCompositor::~PaintArtifactCompositor() {
+  // TODO(crbug.com/836897, crbug.com/836912):
+  // In BlinkGenPropertyTrees mode, some of the layers passed from Blink core
+  // have pre-filled element ID. Need to figure out what is the best place to
+  // setup them.
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
+    return;
   for (auto child : root_layer_->children())
     DCHECK(!child->element_id());
 }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 210d6c3..1c89c15 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -48,7 +48,7 @@
 #include "gpu/config/gpu_feature_info.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_compositor_support.h"
-#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/public/platform/web_layer.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -190,8 +190,12 @@
 DrawingBuffer::~DrawingBuffer() {
   DCHECK(destruction_in_progress_);
   SwapPreviousFrameCallback(nullptr);
-  layer_.reset();
-  context_provider_.reset();
+  if (layer_) {
+    web_layer_ = nullptr;
+    layer_->ClearClient();
+    layer_ = nullptr;
+  }
+  context_provider_ = nullptr;
 }
 
 bool DrawingBuffer::MarkContentsChanged() {
@@ -865,11 +869,10 @@
 
 WebLayer* DrawingBuffer::PlatformLayer() {
   if (!layer_) {
-    layer_ =
-        Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
-            this);
+    layer_ = cc::TextureLayer::CreateForMailbox(this);
 
-    layer_->SetOpaque(!want_alpha_channel_);
+    layer_->SetIsDrawable(true);
+    layer_->SetContentsOpaque(!want_alpha_channel_);
     layer_->SetBlendBackgroundColor(want_alpha_channel_);
     // If premultiplied_alpha_false_texture_ exists, then premultiplied_alpha_
     // has already been handled via CopySubTextureCHROMIUM -- the alpha channel
@@ -884,10 +887,14 @@
     layer_->SetPremultipliedAlpha(premultiplied_alpha_ ||
                                   premultiplied_alpha_false_texture_);
     layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
-    GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+
+    web_layer_ =
+        Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+            layer_.get());
+    GraphicsLayer::RegisterContentsLayer(web_layer_.get());
   }
 
-  return layer_->Layer();
+  return web_layer_.get();
 }
 
 void DrawingBuffer::ClearPlatformLayer() {
@@ -932,7 +939,7 @@
   fbo_ = 0;
 
   if (layer_)
-    GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+    GraphicsLayer::UnregisterContentsLayer(web_layer_.get());
 
   client_ = nullptr;
 }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 3242b99e..73d6c04 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -64,7 +64,6 @@
 class CanvasColorParams;
 class Extensions3DUtil;
 class StaticBitmapImage;
-class WebExternalTextureLayer;
 class WebGraphicsContext3DProvider;
 class WebGraphicsContext3DProviderWrapper;
 class WebLayer;
@@ -563,7 +562,8 @@
   bool is_hidden_ = false;
   SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
 
-  std::unique_ptr<WebExternalTextureLayer> layer_;
+  scoped_refptr<cc::TextureLayer> layer_;
+  std::unique_ptr<WebLayer> web_layer_;  // Wrapper for |layer_|.
 
   // Mailboxes that were released by the compositor can be used again by this
   // DrawingBuffer.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
index ff9dd43c..1eb9dd0 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h"
 
+#include "cc/layers/texture_layer.h"
 #include "cc/resources/cross_thread_shared_bitmap.h"
 #include "components/viz/common/quads/shared_bitmap.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
@@ -11,25 +12,28 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_compositor_support.h"
-#include "third_party/blink/public/platform/web_external_texture_layer.h"
 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/public/platform/web_layer.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/color_behavior.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace blink {
 
 ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode)
     : opacity_mode_(opacity_mode) {
-  layer_ = Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
-      this);
-  GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+  layer_ = cc::TextureLayer::CreateForMailbox(this);
+  layer_->SetIsDrawable(true);
   layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
   if (opacity_mode_ == kOpaque) {
-    layer_->SetOpaque(true);
+    layer_->SetContentsOpaque(true);
     layer_->SetBlendBackgroundColor(false);
   }
+  web_layer_ = Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+      layer_.get());
+  GraphicsLayer::RegisterContentsLayer(web_layer_.get());
 }
 
 ImageLayerBridge::~ImageLayerBridge() {
@@ -44,7 +48,7 @@
   image_ = std::move(image);
   if (image_) {
     if (opacity_mode_ == kNonOpaque) {
-      layer_->SetOpaque(image_->CurrentFrameKnownToBeOpaque());
+      layer_->SetContentsOpaque(image_->CurrentFrameKnownToBeOpaque());
       layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque());
     }
   }
@@ -61,20 +65,20 @@
   has_presented_since_last_set_image_ = false;
 }
 
-void ImageLayerBridge::SetUV(const FloatPoint left_top,
-                             const FloatPoint right_bottom) {
+void ImageLayerBridge::SetUV(const FloatPoint& left_top,
+                             const FloatPoint& right_bottom) {
   if (disposed_)
     return;
 
-  layer_->SetUV(WebFloatPoint(left_top.X(), left_top.Y()),
-                WebFloatPoint(right_bottom.X(), right_bottom.Y()));
+  layer_->SetUV(left_top, right_bottom);
 }
 
 void ImageLayerBridge::Dispose() {
   if (layer_) {
-    GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+    GraphicsLayer::UnregisterContentsLayer(web_layer_.get());
     layer_->ClearTexture();
-    layer_.reset();
+    web_layer_ = nullptr;
+    layer_ = nullptr;
   }
   image_ = nullptr;
   disposed_ = true;
@@ -216,7 +220,7 @@
 }
 
 WebLayer* ImageLayerBridge::PlatformLayer() const {
-  return layer_->Layer();
+  return web_layer_.get();
 }
 
 ImageLayerBridge::RegisteredBitmap::RegisteredBitmap() = default;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
index b58dbb53..5b39341 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
@@ -14,16 +14,18 @@
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace cc {
 class CrossThreadSharedBitmap;
+class TextureLayer;
+}
+
+namespace gfx {
+class Size;
 }
 
 namespace blink {
-
 class WebLayer;
-class WebExternalTextureLayer;
 
 class PLATFORM_EXPORT ImageLayerBridge
     : public GarbageCollectedFinalized<ImageLayerBridge>,
@@ -51,7 +53,7 @@
   void SetFilterQuality(SkFilterQuality filter_quality) {
     filter_quality_ = filter_quality;
   }
-  void SetUV(const FloatPoint left_top, const FloatPoint right_bottom);
+  void SetUV(const FloatPoint& left_top, const FloatPoint& right_bottom);
 
   bool IsAccelerated() { return image_->IsTextureBacked(); }
 
@@ -85,7 +87,8 @@
                                 bool lost_resource);
 
   scoped_refptr<StaticBitmapImage> image_;
-  std::unique_ptr<WebExternalTextureLayer> layer_;
+  scoped_refptr<cc::TextureLayer> layer_;
+  std::unique_ptr<WebLayer> web_layer_;  // Wrapper for |layer_|.
   SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
 
   // SharedMemory bitmaps that can be recycled.
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 9a40207..1b71d1d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -226,6 +226,7 @@
     SetContentsTo(layer, prevent_contents_opaque_changes);
   }
   bool HasContentsLayer() const { return contents_layer_; }
+  WebLayer* ContentsLayer() const { return contents_layer_; }
 
   // For hosting this GraphicsLayer in a native layer hierarchy.
   WebLayer* PlatformLayer() const;
@@ -277,9 +278,6 @@
 
   PaintController& GetPaintController() const;
 
-  // Exposed for tests.
-  WebLayer* ContentsLayer() const { return contents_layer_; }
-
   void SetElementId(const CompositorElementId&);
   CompositorElementId GetElementId() const;
 
diff --git a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
index b1c83dd..e7fe70e 100644
--- a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
+++ b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
@@ -151,6 +151,9 @@
   TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index",
                static_cast<int>(index));
 
+  // Lock the mutex, so only one thread can use the decoder at once.
+  MutexLocker lock(decode_mutex_);
+
   // This implementation does not support arbitrary scaling so check the
   // requested size.
   SkISize scaled_size = SkISize::Make(info.width(), info.height());
@@ -226,13 +229,14 @@
     const SkISize& scaled_size,
     SkBitmap::Allocator& allocator,
     ImageDecoder::AlphaOption alpha_option) {
+#if DCHECK_IS_ON()
+  DCHECK(decode_mutex_.Locked());
+#endif
+
   TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecode", "frame index",
                static_cast<int>(index));
 
   ImageDecoder* decoder = nullptr;
-
-  // Lock the mutex, so only one thread can use the decoder at once.
-  MutexLocker lock(decode_mutex_);
   const bool resume_decoding = ImageDecodingStore::Instance().LockDecoder(
       this, scaled_size, alpha_option, &decoder);
   DCHECK(!resume_decoding || decoder);
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.cc b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
index 9d7fb821..e154076 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.cc
@@ -125,6 +125,7 @@
     DEBUG_STRING_CASE(ForeignLayerPlugin);
     DEBUG_STRING_CASE(ForeignLayerVideo);
     DEBUG_STRING_CASE(ForeignLayerWrapper);
+    DEBUG_STRING_CASE(ForeignLayerContentsWrapper);
     DEFAULT_CASE;
   }
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item.h b/third_party/blink/renderer/platform/graphics/paint/display_item.h
index 80ecd92..96e856e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item.h
@@ -120,7 +120,8 @@
     kForeignLayerPlugin,
     kForeignLayerVideo,
     kForeignLayerWrapper,
-    kForeignLayerLast = kForeignLayerWrapper,
+    kForeignLayerContentsWrapper,
+    kForeignLayerLast = kForeignLayerContentsWrapper,
 
     kClipFirst,
     kClipBoxPaintPhaseFirst = kClipFirst,
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index 4614d56..a7fea929 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -51,6 +51,8 @@
     "heap_linked_stack.h",
     "heap_page.cc",
     "heap_page.h",
+    "heap_stats_collector.cc",
+    "heap_stats_collector.h",
     "heap_terminated_array.h",
     "heap_terminated_array_builder.h",
     "heap_traits.h",
@@ -111,6 +113,7 @@
     "address_cache_test.cc",
     "blink_gc_memory_dump_provider_test.cc",
     "heap_compact_test.cc",
+    "heap_stats_collector_test.cc",
     "heap_test.cc",
     "heap_test_utilities.cc",
     "heap_test_utilities.h",
diff --git a/third_party/blink/renderer/platform/heap/blink_gc.h b/third_party/blink/renderer/platform/heap/blink_gc.h
index 65d62de2..1c2f5a50d 100644
--- a/third_party/blink/renderer/platform/heap/blink_gc.h
+++ b/third_party/blink/renderer/platform/heap/blink_gc.h
@@ -97,14 +97,15 @@
   };
 
   enum GCReason {
-    kIdleGC,
-    kPreciseGC,
-    kConservativeGC,
-    kForcedGC,
-    kMemoryPressureGC,
-    kPageNavigationGC,
-    kThreadTerminationGC,
-    kLastGCReason = kThreadTerminationGC,
+    kIdleGC = 0,
+    kPreciseGC = 1,
+    kConservativeGC = 2,
+    kForcedGC = 3,
+    kMemoryPressureGC = 4,
+    kPageNavigationGC = 5,
+    kThreadTerminationGC = 6,
+    kTesting = 7,
+    kLastGCReason = kTesting,
   };
 
   enum ArenaIndices {
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 77c074f..c752d60 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/platform/heap/address_cache.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/page_memory.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
@@ -128,6 +129,7 @@
 
 ThreadHeap::ThreadHeap(ThreadState* thread_state)
     : thread_state_(thread_state),
+      heap_stats_collector_(std::make_unique<ThreadHeapStatsCollector>()),
       region_tree_(std::make_unique<RegionTree>()),
       address_cache_(std::make_unique<AddressCache>()),
       free_page_pool_(std::make_unique<PagePool>()),
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index bb25750b..282f2936 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -56,6 +56,7 @@
 }  // namespace incremental_marking_test
 
 class AddressCache;
+class ThreadHeapStatsCollector;
 class PagePool;
 class RegionTree;
 
@@ -463,6 +464,10 @@
   enum SnapshotType { kHeapSnapshot, kFreelistSnapshot };
   void TakeSnapshot(SnapshotType);
 
+  ThreadHeapStatsCollector* stats_collector() const {
+    return heap_stats_collector_.get();
+  }
+
 #if defined(ADDRESS_SANITIZER)
   void PoisonEagerArena();
   void PoisonAllHeaps();
@@ -496,6 +501,7 @@
 
   ThreadState* thread_state_;
   ThreadHeapStats stats_;
+  std::unique_ptr<ThreadHeapStatsCollector> heap_stats_collector_;
   std::unique_ptr<RegionTree> region_tree_;
   std::unique_ptr<AddressCache> address_cache_;
   std::unique_ptr<PagePool> free_page_pool_;
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 181745da..1e2fdab 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/platform/heap/address_cache.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
 #include "third_party/blink/renderer/platform/heap/marking_verifier.h"
 #include "third_party/blink/renderer/platform/heap/page_memory.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
@@ -265,16 +266,16 @@
   if (GetThreadState()->SweepForbidden())
     return nullptr;
 
-  TRACE_EVENT0("blink_gc", "BaseArena::lazySweepPages");
-  ThreadState::SweepForbiddenScope sweep_forbidden(GetThreadState());
-  ScriptForbiddenScope script_forbidden;
-
-  double start_time = WTF::CurrentTimeTicksInMilliseconds();
-  Address result = LazySweepPages(allocation_size, gc_info_index);
-  GetThreadState()->AccumulateSweepingTime(
-      WTF::CurrentTimeTicksInMilliseconds() - start_time);
+  Address result = nullptr;
+  {
+    ThreadHeapStatsCollector::Scope stats_scope(
+        GetThreadState()->Heap().stats_collector(),
+        ThreadHeapStatsCollector::Scope::kLazySweepOnAllocation);
+    ThreadState::SweepForbiddenScope sweep_forbidden(GetThreadState());
+    ScriptForbiddenScope script_forbidden;
+    result = LazySweepPages(allocation_size, gc_info_index);
+  }
   ThreadHeap::ReportMemoryUsageForTracing();
-
   return result;
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index 89943847..39b379c3 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -954,11 +954,9 @@
 #if defined(OS_WIN)
   static const uintptr_t random2 =
       ~(RotateLeft16(reinterpret_cast<uintptr_t>(::ReadFile)));
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   static const uintptr_t random2 =
       ~(RotateLeft16(reinterpret_cast<uintptr_t>(::read)));
-#else
-#error OS not supported
 #endif
 
 #if defined(ARCH_CPU_64_BITS)
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
new file mode 100644
index 0000000..78b62431
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
@@ -0,0 +1,105 @@
+// 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 "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+ThreadHeapStatsCollector::Scope::Scope(ThreadHeapStatsCollector* tracer,
+                                       ThreadHeapStatsCollector::Scope::Id id)
+    : tracer_(tracer),
+      start_time_(WTF::CurrentTimeTicksInMilliseconds()),
+      id_(id) {
+  TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+                     ThreadHeapStatsCollector::Scope::ToString(id_));
+}
+
+ThreadHeapStatsCollector::Scope::~Scope() {
+  TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+                   ThreadHeapStatsCollector::Scope::ToString(id_));
+  tracer_->IncreaseScopeTime(
+      id_, WTF::CurrentTimeTicksInMilliseconds() - start_time_);
+}
+
+const char* ThreadHeapStatsCollector::Scope::ToString(
+    ThreadHeapStatsCollector::Scope::Id id) {
+  switch (id) {
+    case Scope::kCompleteSweep:
+      return "BlinkGC.CompleteSweep";
+    case Scope::kEagerSweep:
+      return "BlinkGC.EagerSweep";
+    case Scope::kIncrementalMarkingStartMarking:
+      return "BlinkGC.IncrementalMarkingStartMarking";
+    case Scope::kIncrementalMarkingStep:
+      return "BlinkGC.IncrementalMarkingStep";
+    case Scope::kIncrementalMarkingFinalize:
+      return "BlinkGC.IncrementalMarkingFinalize";
+    case Scope::kIncrementalMarkingFinalizeMarking:
+      return "BlinkGC.IncrementalMarkingFinalizeMarking";
+    case Scope::kLazySweepInIdle:
+      return "BlinkGC.LazySweepInIdle";
+    case Scope::kLazySweepOnAllocation:
+      return "BlinkGC.LazySweepOnAllocation";
+    case Scope::kFullGCMarking:
+      return "BlinkGC.FullGCMarking";
+    case Scope::kNumIds:
+      break;
+  }
+  CHECK(false);
+  return nullptr;
+}
+
+void ThreadHeapStatsCollector::IncreaseScopeTime(
+    ThreadHeapStatsCollector::Scope::Id id,
+    double time) {
+  DCHECK(is_started_);
+  current_.scope_data[id] += time;
+}
+
+void ThreadHeapStatsCollector::IncreaseMarkedObjectSize(size_t size) {
+  DCHECK(is_started_);
+  current_.marked_object_size += size;
+}
+
+void ThreadHeapStatsCollector::Start(BlinkGC::GCReason reason) {
+  DCHECK(!is_started_);
+  is_started_ = true;
+  current_.reason = reason;
+}
+
+void ThreadHeapStatsCollector::Stop() {
+  is_started_ = false;
+  previous_ = std::move(current_);
+  current_.reset();
+}
+
+void ThreadHeapStatsCollector::Event::reset() {
+  marked_object_size = 0;
+  memset(scope_data, 0, sizeof(scope_data));
+  reason = BlinkGC::kTesting;
+}
+
+double ThreadHeapStatsCollector::Event::marking_time_in_ms() const {
+  return scope_data[Scope::kIncrementalMarkingStartMarking] +
+         scope_data[Scope::kIncrementalMarkingStep] +
+         scope_data[Scope::kIncrementalMarkingFinalizeMarking] +
+         scope_data[Scope::kFullGCMarking];
+}
+
+double ThreadHeapStatsCollector::Event::marking_time_per_byte_in_s() const {
+  return marked_object_size ? marking_time_in_ms() / 1000 / marked_object_size
+                            : 0.0;
+}
+
+double ThreadHeapStatsCollector::Event::sweeping_time_in_ms() const {
+  return scope_data[Scope::kCompleteSweep] + scope_data[Scope::kEagerSweep] +
+         scope_data[Scope::kLazySweepInIdle] +
+         scope_data[Scope::kLazySweepOnAllocation];
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
new file mode 100644
index 0000000..ef5776a
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
@@ -0,0 +1,85 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_STATS_COLLECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_STATS_COLLECTOR_H_
+
+#include <stddef.h>
+
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// Manages counters and statistics across garbage collection cycles.
+//
+// Usage:
+//   ThreadHeapStatsCollector stats_collector;
+//   stats_collector.Start(<BlinkGC::GCReason>);
+//   // Use tracer.
+//   // Current event is available using stats_collector.current().
+//   stats_collector.Stop();
+//   // Previous event is available using stats_collector.previous().
+class PLATFORM_EXPORT ThreadHeapStatsCollector {
+ public:
+  // Trace a particular scope. Will emit a trace event and record the time in
+  // the corresponding ThreadHeapStatsCollector.
+  class PLATFORM_EXPORT Scope {
+   public:
+    // These ids will form human readable names when used in Scopes.
+    enum Id {
+      kCompleteSweep,
+      kEagerSweep,
+      kIncrementalMarkingStartMarking,
+      kIncrementalMarkingStep,
+      kIncrementalMarkingFinalize,
+      kIncrementalMarkingFinalizeMarking,
+      kLazySweepInIdle,
+      kLazySweepOnAllocation,
+      kFullGCMarking,
+      kNumIds,
+    };
+
+    static const char* ToString(Id);
+
+    Scope(ThreadHeapStatsCollector*, Id);
+    ~Scope();
+
+   private:
+    ThreadHeapStatsCollector* const tracer_;
+    const double start_time_;
+    const Id id_;
+  };
+
+  struct PLATFORM_EXPORT Event {
+    void reset();
+
+    double marking_time_in_ms() const;
+    double marking_time_per_byte_in_s() const;
+    double sweeping_time_in_ms() const;
+
+    size_t marked_object_size = 0;
+    double scope_data[Scope::kNumIds] = {0};
+    BlinkGC::GCReason reason;
+  };
+
+  void Start(BlinkGC::GCReason);
+  void Stop();
+
+  void IncreaseScopeTime(Scope::Id, double);
+  void IncreaseMarkedObjectSize(size_t);
+
+  bool is_started() const { return is_started_; }
+  const Event& current() const { return current_; }
+  const Event& previous() const { return previous_; }
+
+ private:
+  Event current_;
+  Event previous_;
+  bool is_started_ = false;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_STATS_COLLECTOR_H_
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
new file mode 100644
index 0000000..9db1958
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
@@ -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.
+
+#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+// =============================================================================
+// ThreadHeapStatsCollector. ===================================================
+// =============================================================================
+
+TEST(ThreadHeapStatsCollectorTest, InitialEmpty) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  for (int i = 0; i < ThreadHeapStatsCollector::Scope::Id::kNumIds; i++) {
+    EXPECT_DOUBLE_EQ(0.0, stats_collector.current().scope_data[i]);
+  }
+  stats_collector.Stop();
+}
+
+TEST(ThreadHeapStatsCollectorTest, IncreaseScopeTime) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep, 1.0);
+  EXPECT_DOUBLE_EQ(
+      1.0, stats_collector.current().scope_data
+               [ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep]);
+}
+
+TEST(ThreadHeapStatsCollectorTest, StopMovesCurrentToPrevious) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep, 1.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(
+      1.0, stats_collector.previous().scope_data
+               [ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep]);
+}
+
+TEST(ThreadHeapStatsCollectorTest, StopResetsCurrent) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep, 1.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(
+      0.0, stats_collector.current().scope_data
+               [ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep]);
+}
+
+TEST(ThreadHeapStatsCollectorTest, StartStop) {
+  ThreadHeapStatsCollector stats_collector;
+  EXPECT_FALSE(stats_collector.is_started());
+  stats_collector.Start(BlinkGC::kTesting);
+  EXPECT_TRUE(stats_collector.is_started());
+  stats_collector.Stop();
+  EXPECT_FALSE(stats_collector.is_started());
+}
+
+// =============================================================================
+// ThreadHeapStatsCollector::Scope. ============================================
+// =============================================================================
+
+TEST(ThreadHeapStatsCollectorTest, ScopeToString) {
+  EXPECT_STREQ(
+      "BlinkGC.IncrementalMarkingStartMarking",
+      ThreadHeapStatsCollector::Scope::ToString(
+          ThreadHeapStatsCollector::Scope::kIncrementalMarkingStartMarking));
+}
+
+// =============================================================================
+// ThreadHeapStatsCollector::Event. ============================================
+// =============================================================================
+
+TEST(ThreadHeapStatsCollectorTest, EventMarkedObjectSize) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseMarkedObjectSize(1024);
+  stats_collector.Stop();
+  EXPECT_EQ(1024u, stats_collector.previous().marked_object_size);
+}
+
+TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsFromIncrementalGC) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStartMarking, 7.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep, 2.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingFinalizeMarking, 1.0);
+  // Ignore the full finalization.
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingFinalize, 3.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(10.0, stats_collector.previous().marking_time_in_ms());
+}
+
+TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsFromFullGC) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kFullGCMarking, 11.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(11.0, stats_collector.previous().marking_time_in_ms());
+}
+
+TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseMarkedObjectSize(1000);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kFullGCMarking, 1000.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(.001,
+                   stats_collector.previous().marking_time_per_byte_in_s());
+}
+
+TEST(ThreadHeapStatsCollectorTest, SweepingTimeInMs) {
+  ThreadHeapStatsCollector stats_collector;
+  stats_collector.Start(BlinkGC::kTesting);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kLazySweepInIdle, 1.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kLazySweepInIdle, 2.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kLazySweepInIdle, 3.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kLazySweepOnAllocation, 4.0);
+  stats_collector.IncreaseScopeTime(
+      ThreadHeapStatsCollector::Scope::kCompleteSweep, 5.0);
+  stats_collector.Stop();
+  EXPECT_DOUBLE_EQ(15.0, stats_collector.previous().sweeping_time_in_ms());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index bc33272d..9d108d9 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_linked_stack.h"
+#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
 #include "third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h"
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
@@ -365,6 +366,7 @@
       : TestGCCollectGarbageScope(state),
         atomic_pause_scope_(ThreadState::Current()),
         persistent_lock_(ProcessHeap::CrossThreadPersistentMutex()) {
+    ThreadState::Current()->Heap().stats_collector()->Start(BlinkGC::kTesting);
     ThreadState::Current()->MarkPhasePrologue(state, BlinkGC::kAtomicMarking,
                                               BlinkGC::kPreciseGC);
   }
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 31d89242..8a2cf71 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
 #include "third_party/blink/renderer/platform/heap/safe_point.h"
@@ -106,6 +107,8 @@
       return "PageNavigationGC";
     case BlinkGC::kThreadTerminationGC:
       return "ThreadTerminationGC";
+    case BlinkGC::kTesting:
+      return "TestingGC";
   }
   return "<Unknown>";
 }
@@ -155,7 +158,6 @@
       no_allocation_count_(0),
       gc_forbidden_count_(0),
       mixins_being_constructed_count_(0),
-      accumulated_sweeping_time_(0),
       object_resurrection_forbidden_(false),
       in_atomic_pause_(false),
       gc_mixin_marker_(nullptr),
@@ -655,20 +657,22 @@
   RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(
       GetIsolate(), RuntimeCallStats::CounterId::kPerformIdleLazySweep);
 
-  TRACE_EVENT1("blink_gc,devtools.timeline",
-               "ThreadState::performIdleLazySweep", "idleDeltaInSeconds",
-               deadline_seconds - CurrentTimeTicksInSeconds());
-
-  AtomicPauseScope atomic_pause_scope(this);
-  SweepForbiddenScope scope(this);
-
-  double start_time = WTF::CurrentTimeTicksInMilliseconds();
-  bool sweep_completed = Heap().AdvanceLazySweep(deadline_seconds);
-  // We couldn't finish the sweeping within the deadline.
-  // We request another idle task for the remaining sweeping.
-  if (!sweep_completed)
-    ScheduleIdleLazySweep();
-  AccumulateSweepingTime(WTF::CurrentTimeTicksInMilliseconds() - start_time);
+  bool sweep_completed = false;
+  {
+    AtomicPauseScope atomic_pause_scope(this);
+    SweepForbiddenScope scope(this);
+    ThreadHeapStatsCollector::Scope stats_scope(
+        Heap().stats_collector(),
+        ThreadHeapStatsCollector::Scope::kLazySweepInIdle);
+    TRACE_EVENT1("blink_gc,devtools.timeline",
+                 "ThreadState::performIdleLazySweep", "idleDeltaInSeconds",
+                 deadline_seconds - CurrentTimeTicksInSeconds());
+    sweep_completed = Heap().AdvanceLazySweep(deadline_seconds);
+    // We couldn't finish the sweeping within the deadline.
+    // We request another idle task for the remaining sweeping.
+    if (!sweep_completed)
+      ScheduleIdleLazySweep();
+  }
 
   if (sweep_completed)
     PostSweep();
@@ -882,6 +886,7 @@
     gc_state_ = kNoGCScheduled;
     SetGCPhase(GCPhase::kSweeping);
     SetGCPhase(GCPhase::kNone);
+    Heap().stats_collector()->Stop();
     return;
   }
 
@@ -894,8 +899,6 @@
   // a dead object gets resurrected.
   InvokePreFinalizers();
 
-  accumulated_sweeping_time_ = 0;
-
   EagerSweep();
 
   // Any sweep compaction must happen after pre-finalizers and eager
@@ -925,12 +928,10 @@
   // by lazy sweeping. Keep those in a designated heap and sweep it
   // eagerly.
   DCHECK(IsSweepingInProgress());
-
   SweepForbiddenScope scope(this);
-
-  double start_time = WTF::CurrentTimeTicksInMilliseconds();
+  ThreadHeapStatsCollector::Scope stats_scope(
+      Heap().stats_collector(), ThreadHeapStatsCollector::Scope::kEagerSweep);
   Heap().Arena(BlinkGC::kEagerSweepArenaIndex)->CompleteSweep();
-  AccumulateSweepingTime(WTF::CurrentTimeTicksInMilliseconds() - start_time);
 }
 
 void ThreadState::CompleteSweep() {
@@ -945,24 +946,15 @@
   if (SweepForbidden())
     return;
 
-  AtomicPauseScope atomic_pause_scope(this);
-  SweepForbiddenScope scope(this);
-
-  TRACE_EVENT0("blink_gc,devtools.timeline", "ThreadState::completeSweep");
-  double start_time = WTF::CurrentTimeTicksInMilliseconds();
-
-  Heap().CompleteSweep();
-
-  double time_for_complete_sweep =
-      WTF::CurrentTimeTicksInMilliseconds() - start_time;
-  AccumulateSweepingTime(time_for_complete_sweep);
-
-  if (IsMainThread()) {
-    DEFINE_STATIC_LOCAL(CustomCountHistogram, complete_sweep_histogram,
-                        ("BlinkGC.CompleteSweep", 1, 10 * 1000, 50));
-    complete_sweep_histogram.Count(time_for_complete_sweep);
+  {
+    AtomicPauseScope atomic_pause_scope(this);
+    SweepForbiddenScope scope(this);
+    ThreadHeapStatsCollector::Scope stats_scope(
+        Heap().stats_collector(),
+        ThreadHeapStatsCollector::Scope::kCompleteSweep);
+    TRACE_EVENT0("blink_gc,devtools.timeline", "ThreadState::completeSweep");
+    Heap().CompleteSweep();
   }
-
   PostSweep();
 }
 
@@ -975,6 +967,31 @@
   thread_state_->RemoveObserver(this);
 }
 
+namespace {
+
+void UpdateHistograms(const ThreadHeapStatsCollector::Event& event) {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      EnumerationHistogram, gc_reason_histogram,
+      ("BlinkGC.GCReason", BlinkGC::kLastGCReason + 1));
+  gc_reason_histogram.Count(event.reason);
+
+  // TODO(mlippautz): Update name of this histogram.
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, marking_time_histogram,
+                                  ("BlinkGC.CollectGarbage", 0, 10 * 1000, 50));
+  marking_time_histogram.Count(event.marking_time_in_ms());
+
+  DEFINE_STATIC_LOCAL(CustomCountHistogram, complete_sweep_histogram,
+                      ("BlinkGC.CompleteSweep", 1, 10 * 1000, 50));
+  complete_sweep_histogram.Count(
+      event.scope_data[ThreadHeapStatsCollector::Scope::kCompleteSweep]);
+
+  DEFINE_STATIC_LOCAL(CustomCountHistogram, time_for_sweep_histogram,
+                      ("BlinkGC.TimeForSweepingAllObjects", 1, 10 * 1000, 50));
+  time_for_sweep_histogram.Count(event.sweeping_time_in_ms());
+}
+
+}  // namespace
+
 void ThreadState::PostSweep() {
   DCHECK(CheckThread());
   ThreadHeap::ReportMemoryUsageForTracing();
@@ -1007,10 +1024,6 @@
     DEFINE_STATIC_LOCAL(CustomCountHistogram, collection_rate_histogram,
                         ("BlinkGC.CollectionRate", 1, 100, 20));
     collection_rate_histogram.Count(static_cast<int>(100 * collection_rate));
-    DEFINE_STATIC_LOCAL(
-        CustomCountHistogram, time_for_sweep_histogram,
-        ("BlinkGC.TimeForSweepingAllObjects", 1, 10 * 1000, 50));
-    time_for_sweep_histogram.Count(accumulated_sweeping_time_);
 
 #define COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(GCReason)              \
   case BlinkGC::k##GCReason: {                                              \
@@ -1040,6 +1053,10 @@
 
   for (auto* const observer : observers_)
     observer->OnCompleteSweepDone();
+
+  Heap().stats_collector()->Stop();
+  if (IsMainThread())
+    UpdateHistograms(Heap().stats_collector()->previous());
 }
 
 void ThreadState::SafePoint(BlinkGC::StackState stack_state) {
@@ -1274,16 +1291,27 @@
   VLOG(2) << "[state:" << this << "] "
           << "IncrementalMarking: Start";
   CompleteSweep();
-  AtomicPauseScope atomic_pause_scope(this);
-  MarkPhasePrologue(BlinkGC::kNoHeapPointersOnStack,
-                    BlinkGC::kIncrementalMarking, BlinkGC::kIdleGC);
-  MarkPhaseVisitRoots();
-  EnableIncrementalMarkingBarrier();
-  ScheduleIncrementalMarkingStep();
-  DCHECK(IsMarkingInProgress());
+  // TODO(mlippautz): Replace this with a proper reason once incremental marking
+  // is actually scheduled in production.
+  Heap().stats_collector()->Start(BlinkGC::kTesting);
+  {
+    ThreadHeapStatsCollector::Scope stats_scope(
+        Heap().stats_collector(),
+        ThreadHeapStatsCollector::Scope::kIncrementalMarkingStartMarking);
+    AtomicPauseScope atomic_pause_scope(this);
+    MarkPhasePrologue(BlinkGC::kNoHeapPointersOnStack,
+                      BlinkGC::kIncrementalMarking, BlinkGC::kTesting);
+    MarkPhaseVisitRoots();
+    EnableIncrementalMarkingBarrier();
+    ScheduleIncrementalMarkingStep();
+    DCHECK(IsMarkingInProgress());
+  }
 }
 
 void ThreadState::IncrementalMarkingStep() {
+  ThreadHeapStatsCollector::Scope stats_scope(
+      Heap().stats_collector(),
+      ThreadHeapStatsCollector::Scope::kIncrementalMarkingStep);
   VLOG(2) << "[state:" << this << "] "
           << "IncrementalMarking: Step";
   AtomicPauseScope atomic_pause_scope(this);
@@ -1298,20 +1326,30 @@
 }
 
 void ThreadState::IncrementalMarkingFinalize() {
-  VLOG(2) << "[state:" << this << "] "
-          << "IncrementalMarking: Finalize";
-  SetGCState(kNoGCScheduled);
-  DisableIncrementalMarkingBarrier();
-  AtomicPauseScope atomic_pause_scope(this);
-  DCHECK(IsMarkingInProgress());
-  MarkPhaseVisitRoots();
-  bool complete =
-      MarkPhaseAdvanceMarking(std::numeric_limits<double>::infinity());
-  CHECK(complete);
-  MarkPhaseEpilogue(current_gc_data_.marking_type);
-  PreSweep(current_gc_data_.marking_type, BlinkGC::kLazySweeping);
-  DCHECK(IsSweepingInProgress());
-  DCHECK_EQ(GcState(), kNoGCScheduled);
+  {
+    ThreadHeapStatsCollector::Scope stats_scope(
+        Heap().stats_collector(),
+        ThreadHeapStatsCollector::Scope::kIncrementalMarkingFinalize);
+    VLOG(2) << "[state:" << this << "] "
+            << "IncrementalMarking: Finalize";
+    SetGCState(kNoGCScheduled);
+    DisableIncrementalMarkingBarrier();
+    AtomicPauseScope atomic_pause_scope(this);
+    DCHECK(IsMarkingInProgress());
+    {
+      ThreadHeapStatsCollector::Scope stats_scope(
+          Heap().stats_collector(),
+          ThreadHeapStatsCollector::Scope::kIncrementalMarkingFinalizeMarking);
+      MarkPhaseVisitRoots();
+      bool complete =
+          MarkPhaseAdvanceMarking(std::numeric_limits<double>::infinity());
+      CHECK(complete);
+      MarkPhaseEpilogue(current_gc_data_.marking_type);
+    }
+    PreSweep(current_gc_data_.marking_type, BlinkGC::kLazySweeping);
+    DCHECK(IsSweepingInProgress());
+    DCHECK_EQ(GcState(), kNoGCScheduled);
+  }
 }
 
 void ThreadState::CollectGarbage(BlinkGC::StackState stack_state,
@@ -1327,7 +1365,6 @@
 
   double start_total_collect_garbage_time =
       WTF::CurrentTimeTicksInMilliseconds();
-
   RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(
       GetIsolate(), RuntimeCallStats::CounterId::kCollectGarbage);
 
@@ -1341,8 +1378,8 @@
   }
 
   // We don't want floating garbage for the specific garbage collection types
-  // mentioned below. In this case we will follow up with a regular full garbage
-  // collection.
+  // mentioned below. In this case we will follow up with a regular full
+  // garbage collection.
   const bool should_do_full_gc = !was_incremental_marking ||
                                  reason == BlinkGC::kForcedGC ||
                                  reason == BlinkGC::kMemoryPressureGC ||
@@ -1350,8 +1387,12 @@
   if (should_do_full_gc) {
     CompleteSweep();
     SetGCState(kNoGCScheduled);
+    Heap().stats_collector()->Start(reason);
     AtomicPauseScope atomic_pause_scope(this);
     {
+      ThreadHeapStatsCollector::Scope stats_scope(
+          Heap().stats_collector(),
+          ThreadHeapStatsCollector::Scope::kFullGCMarking);
       TRACE_EVENT2("blink_gc,devtools.timeline", "BlinkGCMarking",
                    "lazySweeping", sweeping_type == BlinkGC::kLazySweeping,
                    "gcReason", GcReasonString(reason));
@@ -1498,9 +1539,6 @@
   if (invalidate_dead_objects_in_wrappers_marking_deque_)
     invalidate_dead_objects_in_wrappers_marking_deque_(isolate_);
 
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, marking_time_histogram,
-                                  ("BlinkGC.CollectGarbage", 0, 10 * 1000, 50));
-  marking_time_histogram.Count(current_gc_data_.marking_time_in_milliseconds);
   DEFINE_THREAD_SAFE_STATIC_LOCAL(
       CustomCountHistogram, total_object_space_histogram,
       ("BlinkGC.TotalObjectSpace", 0, 4 * 1024 * 1024, 50));
@@ -1511,10 +1549,6 @@
       ("BlinkGC.TotalAllocatedSpace", 0, 4 * 1024 * 1024, 50));
   total_allocated_space_histogram.Count(ProcessHeap::TotalAllocatedSpace() /
                                         1024);
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(
-      EnumerationHistogram, gc_reason_histogram,
-      ("BlinkGC.GCReason", BlinkGC::kLastGCReason + 1));
-  gc_reason_histogram.Count(current_gc_data_.reason);
 }
 
 void ThreadState::VerifyMarking(BlinkGC::MarkingType marking_type) {
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 4f0d530..281b001 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -530,10 +530,6 @@
     }
   }
 
-  void AccumulateSweepingTime(double time) {
-    accumulated_sweeping_time_ += time;
-  }
-
   void FreePersistentNode(PersistentRegion*, PersistentNode*);
 
   using PersistentClearCallback = void (*)(void*);
@@ -700,7 +696,6 @@
   size_t no_allocation_count_;
   size_t gc_forbidden_count_;
   size_t mixins_being_constructed_count_;
-  double accumulated_sweeping_time_;
   bool object_resurrection_forbidden_;
   bool in_atomic_pause_;
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e99d3d0..f1af7dd6 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -616,6 +616,9 @@
       status: "experimental",
     },
     {
+      name: "IntersectionObserverV2",
+    },
+    {
       name: "JSImageDecode",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/child/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/child/worker_scheduler.cc
index afb593c2..7ffb883b 100644
--- a/third_party/blink/renderer/platform/scheduler/child/worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/child/worker_scheduler.cc
@@ -71,7 +71,6 @@
     case TaskType::kInternalUserInteraction:
     case TaskType::kInternalInspector:
     case TaskType::kInternalAnimation:
-    case TaskType::kInternalAccessibility:
       // UnthrottledTaskRunner is generally discouraged in future.
       // TODO(nhiroki): Identify which tasks can be throttled / suspendable and
       // move them into other task runners. See also comments in
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index daea9dfbf..d5643a1 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -286,7 +286,6 @@
     case TaskType::kIdleTask:
     case TaskType::kInternalDefault:
     case TaskType::kMiscPlatformAPI:
-    case TaskType::kInternalAccessibility:
       // TODO(altimin): Move appropriate tasks to throttleable task queue.
       return TaskRunnerImpl::Create(DeferrableTaskQueue(), type);
     // PostedMessage can be used for navigation, so we shouldn't defer it
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 52c2acf..2d70578 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -180,8 +180,6 @@
       return "InternalInspector";
     case TaskType::kInternalAnimation:
       return "InternalAnimation";
-    case TaskType::kInternalAccessibility:
-      return "InternalAccessibility";
     case TaskType::kCount:
       return "Count";
   }
diff --git a/third_party/blink/renderer/platform/scroll/scrollable_area.cc b/third_party/blink/renderer/platform/scroll/scrollable_area.cc
index 7234c646..b7141c8 100644
--- a/third_party/blink/renderer/platform/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/platform/scroll/scrollable_area.cc
@@ -158,6 +158,9 @@
 
 ScrollResult ScrollableArea::UserScroll(ScrollGranularity granularity,
                                         const ScrollOffset& delta) {
+  TRACE_EVENT2("input", "ScrollableArea::UserScroll", "x", delta.Width(), "y",
+               delta.Height());
+
   float step_x = ScrollStep(granularity, kHorizontalScrollbar);
   float step_y = ScrollStep(granularity, kVerticalScrollbar);
 
diff --git a/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
index c59088f..d434d1a2 100644
--- a/third_party/blink/renderer/platform/testing/paint_test_configurations.h
+++ b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_
 
 #include <gtest/gtest.h>
+#include "third_party/blink/public/web/web_heap.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
@@ -33,6 +34,10 @@
         ScopedSlimmingPaintV2ForTest(GetParam() & kSlimmingPaintV2),
         ScopedPaintUnderInvalidationCheckingForTest(
             GetParam() & kUnderInvalidationChecking) {}
+  ~PaintTestConfigurations() {
+    // Must destruct all objects before toggling back feature flags.
+    WebHeap::CollectAllGarbageForTesting();
+  }
 };
 
 static constexpr unsigned kAllSlimmingPaintTestConfigurations[] = {
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
index dce88ae7..63622fa1 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -103,10 +103,6 @@
     cc::ContentLayerClient*) {
   return nullptr;
 }
-std::unique_ptr<WebExternalTextureLayer>
-TestingCompositorSupport::CreateExternalTextureLayer(cc::TextureLayerClient*) {
-  return nullptr;
-}
 
 std::unique_ptr<WebImageLayer> TestingCompositorSupport::CreateImageLayer() {
   return nullptr;
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.h b/third_party/blink/renderer/platform/testing/testing_platform_support.h
index 1866a96a..fae881d 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support.h
@@ -58,8 +58,6 @@
   std::unique_ptr<WebLayer> CreateLayerFromCCLayer(cc::Layer*) override;
   std::unique_ptr<WebContentLayer> CreateContentLayer(
       cc::ContentLayerClient*) override;
-  std::unique_ptr<WebExternalTextureLayer> CreateExternalTextureLayer(
-      cc::TextureLayerClient*) override;
   std::unique_ptr<WebImageLayer> CreateImageLayer() override;
   std::unique_ptr<WebScrollbarLayer> CreateScrollbarLayer(
       std::unique_ptr<WebScrollbar>,
diff --git a/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc b/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc
index 6ad79fe..4dbe3b2 100644
--- a/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc
+++ b/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc
@@ -60,7 +60,7 @@
   // TODO(ccameron): This likely causes surface invariant violations.
   layer_tree_host_->SetViewportSizeAndScale(
       gfx_size, layer_tree_host_->device_scale_factor(),
-      layer_tree_host_->local_surface_id());
+      layer_tree_host_->local_surface_id_from_parent());
 }
 
 void WebLayerTreeViewImplForTesting::SetRootLayer(const blink::WebLayer& root) {
diff --git a/third_party/blink/renderer/platform/text/capitalize.cc b/third_party/blink/renderer/platform/text/capitalize.cc
new file mode 100644
index 0000000..858ef83
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/capitalize.cc
@@ -0,0 +1,60 @@
+// 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 "third_party/blink/renderer/platform/text/capitalize.h"
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String Capitalize(const String& string, UChar previous_character) {
+  if (string.IsNull())
+    return string;
+
+  unsigned length = string.length();
+  const StringImpl& input = *string.Impl();
+
+  CHECK_LT(length, std::numeric_limits<unsigned>::max());
+  StringBuffer<UChar> string_with_previous(length + 1);
+  string_with_previous[0] = previous_character == kNoBreakSpaceCharacter
+                                ? kSpaceCharacter
+                                : previous_character;
+  for (unsigned i = 1; i < length + 1; i++) {
+    // Replace &nbsp with a real space since ICU no longer treats &nbsp as a
+    // word separator.
+    if (input[i - 1] == kNoBreakSpaceCharacter)
+      string_with_previous[i] = kSpaceCharacter;
+    else
+      string_with_previous[i] = input[i - 1];
+  }
+
+  TextBreakIterator* boundary =
+      WordBreakIterator(string_with_previous.Characters(), length + 1);
+  if (!boundary)
+    return string;
+
+  StringBuilder result;
+  result.ReserveCapacity(length);
+
+  int32_t end_of_word;
+  int32_t start_of_word = boundary->first();
+  for (end_of_word = boundary->next(); end_of_word != kTextBreakDone;
+       start_of_word = end_of_word, end_of_word = boundary->next()) {
+    if (start_of_word) {  // Ignore first char of previous string
+      result.Append(
+          input[start_of_word - 1] == kNoBreakSpaceCharacter
+              ? kNoBreakSpaceCharacter
+              : WTF::Unicode::ToTitleCase(string_with_previous[start_of_word]));
+    }
+    for (int i = start_of_word + 1; i < end_of_word; i++)
+      result.Append(input[i - 1]);
+  }
+
+  return result.ToString();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/capitalize.h b/third_party/blink/renderer/platform/text/capitalize.h
new file mode 100644
index 0000000..0490579b
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/capitalize.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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CAPITALIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CAPITALIZE_H_
+
+#include <unicode/utypes.h>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+// Capitalize (titlecase) each word of a string.
+// https://drafts.csswg.org/css-text-3/#valdef-text-transform-capitalize
+PLATFORM_EXPORT String Capitalize(const String&,
+                                  UChar previous_character = ' ');
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CAPITALIZE_H_
diff --git a/third_party/blink/renderer/platform/text/capitalize_test.cc b/third_party/blink/renderer/platform/text/capitalize_test.cc
new file mode 100644
index 0000000..ffa15f97
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/capitalize_test.cc
@@ -0,0 +1,35 @@
+// 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 "third_party/blink/renderer/platform/text/capitalize.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+struct CapitalizeTestData {
+  String input;
+  String expected;
+  UChar previous_character = kSpaceCharacter;
+};
+
+class CapitalizeTest : public testing::Test,
+                       public testing::WithParamInterface<CapitalizeTestData> {
+};
+
+INSTANTIATE_TEST_CASE_P(CapitalizeTest,
+                        CapitalizeTest,
+                        testing::Values(CapitalizeTestData{String(), String()},
+                                        CapitalizeTestData{"", ""},
+                                        CapitalizeTestData{"hello, world",
+                                                           "Hello, World"}));
+
+TEST_P(CapitalizeTest, Data) {
+  const auto& data = GetParam();
+  EXPECT_EQ(data.expected, Capitalize(data.input, data.previous_character));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/text_run_test.cc b/third_party/blink/renderer/platform/text/text_run_test.cc
index 4dd4b1e0..4ec66b1 100644
--- a/third_party/blink/renderer/platform/text/text_run_test.cc
+++ b/third_party/blink/renderer/platform/text/text_run_test.cc
@@ -8,29 +8,23 @@
 
 namespace blink {
 
-#if defined(THREAD_SANITIZER)
-#define MAYBE_IndexOfSubRun DISABLED_IndexOfSubRun  // https://crbug.com/830648
-#else
-#define MAYBE_IndexOfSubRun IndexOfSubRun
-#endif
-
-TEST(TextRunTest, MAYBE_IndexOfSubRun) {
-  TextRun run(String("1234567890"));
+TEST(TextRunTest, IndexOfSubRun) {
+  TextRun run("1234567890");
   EXPECT_EQ(0u, run.IndexOfSubRun(run.SubRun(0, 4)));
   EXPECT_EQ(4u, run.IndexOfSubRun(run.SubRun(4, 4)));
   EXPECT_EQ(6u, run.IndexOfSubRun(run.SubRun(6, 4)));
   const unsigned kNotSubRun = std::numeric_limits<unsigned>::max();
   EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(run.SubRun(7, 4)));
-  EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun(String("1"))));
-  EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun(String(u"1"))));
+  EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun("1")));
+  EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun(u"1")));
 
-  TextRun run16(String(u"1234567890"));
+  TextRun run16(u"1234567890");
   EXPECT_EQ(0u, run16.IndexOfSubRun(run16.SubRun(0, 4)));
   EXPECT_EQ(4u, run16.IndexOfSubRun(run16.SubRun(4, 4)));
   EXPECT_EQ(6u, run16.IndexOfSubRun(run16.SubRun(6, 4)));
   EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(run16.SubRun(7, 4)));
-  EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun(String("1"))));
-  EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun(String(u"1"))));
+  EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun("1")));
+  EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun(u"1")));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/web_thread.cc b/third_party/blink/renderer/platform/web_thread.cc
index 15d7408..5d100e6 100644
--- a/third_party/blink/renderer/platform/web_thread.cc
+++ b/third_party/blink/renderer/platform/web_thread.cc
@@ -10,7 +10,7 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <unistd.h>
 #endif
 
@@ -36,7 +36,7 @@
 #if defined(OS_WIN)
 static_assert(sizeof(blink::PlatformThreadId) >= sizeof(DWORD),
               "size of platform thread id is too small");
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 static_assert(sizeof(blink::PlatformThreadId) >= sizeof(pid_t),
               "size of platform thread id is too small");
 #else
diff --git a/third_party/blink/renderer/platform/wtf/byte_order.h b/third_party/blink/renderer/platform/wtf/byte_order.h
index 94b64640..c6cb57e8 100644
--- a/third_party/blink/renderer/platform/wtf/byte_order.h
+++ b/third_party/blink/renderer/platform/wtf/byte_order.h
@@ -33,7 +33,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <arpa/inet.h>
 #endif
 
diff --git a/third_party/blink/renderer/platform/wtf/threading_primitives.h b/third_party/blink/renderer/platform/wtf/threading_primitives.h
index 7777fe9..12c24ab 100644
--- a/third_party/blink/renderer/platform/wtf/threading_primitives.h
+++ b/third_party/blink/renderer/platform/wtf/threading_primitives.h
@@ -41,15 +41,19 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
-#endif
-
-#if defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include <pthread.h>
 #endif
 
 namespace WTF {
 
-#if defined(OS_POSIX)
+#if defined(OS_WIN)
+struct PlatformMutex {
+  CRITICAL_SECTION internal_mutex_;
+  size_t recursion_count_;
+};
+typedef CONDITION_VARIABLE PlatformCondition;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 struct PlatformMutex {
   pthread_mutex_t internal_mutex_;
 #if DCHECK_IS_ON()
@@ -57,15 +61,6 @@
 #endif
 };
 typedef pthread_cond_t PlatformCondition;
-#elif defined(OS_WIN)
-struct PlatformMutex {
-  CRITICAL_SECTION internal_mutex_;
-  size_t recursion_count_;
-};
-typedef CONDITION_VARIABLE PlatformCondition;
-#else
-typedef void* PlatformMutex;
-typedef void* PlatformCondition;
 #endif
 
 class WTF_EXPORT MutexBase {
diff --git a/third_party/blink/renderer/platform/wtf/threading_pthreads.cc b/third_party/blink/renderer/platform/wtf/threading_pthreads.cc
index 57e06ed..cf4f983 100644
--- a/third_party/blink/renderer/platform/wtf/threading_pthreads.cc
+++ b/third_party/blink/renderer/platform/wtf/threading_pthreads.cc
@@ -32,7 +32,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
 
 #include <errno.h>
 #include <limits.h>
@@ -270,4 +270,4 @@
 
 }  // namespace WTF
 
-#endif  // defined(OS_POSIX)
+#endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
diff --git a/third_party/blink/tools/blinkpy/common/base85.py b/third_party/blink/tools/blinkpy/common/base85.py
new file mode 100644
index 0000000..7d57140
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/base85.py
@@ -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.
+
+_BASE85_CHARACTERS = ('0123456789'
+                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                      'abcdefghijklmnopqrstuvwxyz'
+                      '!#$%&()*+-;<=>?@^_`{|}~')
+
+# pylint: disable=invalid-name
+_char_to_value = {}
+
+
+def decode_base85(encoded_str):
+    """Decodes a base85 string.
+
+    The input string length must be a multiple of 5, and the resultant
+    binary length is always a multiple of 4.
+    """
+    if len(encoded_str) % 5 != 0:
+        raise ValueError('Input string length is not a multiple of 5; ' +
+                         str(len(encoded_str)))
+    if not _char_to_value:
+        for i, ch in enumerate(_BASE85_CHARACTERS):
+            _char_to_value[ch] = i
+
+    result = ''
+    i = 0
+    while i < len(encoded_str):
+        acc = 0
+        for _ in range(5):
+            ch = encoded_str[i]
+            if ch not in _char_to_value:
+                raise ValueError('Invalid base85 character; "{}"'.format(ch))
+            new_acc = acc * 85 + _char_to_value[ch]
+            assert new_acc >= acc
+            acc = new_acc
+            i += 1
+        for _ in range(4):
+            result += chr(acc >> 24)
+            acc = (acc & 0x00ffffff) << 8
+            assert acc >= 0
+    return result
diff --git a/third_party/blink/tools/blinkpy/common/base85_unittest.py b/third_party/blink/tools/blinkpy/common/base85_unittest.py
new file mode 100644
index 0000000..165c8cb
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/base85_unittest.py
@@ -0,0 +1,28 @@
+# 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 unittest
+
+from blinkpy.common.base85 import decode_base85
+
+
+class TestBase85(unittest.TestCase):
+
+    def test_decode(self):
+        self.assertEqual(decode_base85('cmV?d00001'), 'x\x01\x03\x00\x00\x00\x00\x01')
+
+    def test_decode_invalid_input(self):
+        self.assertRaises(ValueError, decode_base85, '1')
+        self.assertRaises(ValueError, decode_base85, '123456')
+        self.assertRaises(ValueError, decode_base85, ' 2345')
+        self.assertRaises(ValueError, decode_base85, '1234/')
+
+    def test_decode_corners(self):
+        self.assertEqual(decode_base85(''), '')
+        self.assertEqual(decode_base85('00000'), '\x00\x00\x00\x00')
+        self.assertEqual(decode_base85('|NsC0'), '\xFF\xFF\xFF\xFF')
+
+        # acc will be larger than 0xFFFFFFFF.  Such input is invalid.
+        self.assertRaises(ValueError, decode_base85, '|NsC1')
+        self.assertRaises(ValueError, decode_base85, '~~~~~')
diff --git a/third_party/blink/tools/blinkpy/common/pretty_diff.py b/third_party/blink/tools/blinkpy/common/pretty_diff.py
new file mode 100644
index 0000000..6c706746
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/pretty_diff.py
@@ -0,0 +1,492 @@
+# 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.
+
+"""Prettifies 'git diff' output.
+
+prettify_diff() takes a diff string, and returns an HTML string decorating the
+diff.
+
+This code doesn't support other diff commands such as "diff" and "svn diff".
+"""
+
+
+import base64
+import cgi
+import difflib
+import mimetypes
+import re
+import zlib
+
+from blinkpy.common.base85 import decode_base85
+
+
+# The style below is meant to be similar to PolyGerrit.
+_LEADING_HTML = """<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+body {
+  background: white;
+  font-family: "Roboto Mono", Menlo, "Lucida Console", Monaco, monospace;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+  width: 100%;
+  margin-top: 1em;
+}
+td { white-space: pre-wrap; font-size: 14px; }
+.fileheader { position: sticky; top: 0px; }
+.fileheader div {
+  background: #eee;
+  border-bottom: 1px solid #ddd;
+  border-top: 1px solid #ddd;
+  box-sizing: border-box;
+  line-height: 2.25em;
+  padding: 0.2em 1rem 0.2em 1rem;
+}
+.rename { color: #999999; display: block; }
+.fileinfo { background: #fafafa; color: #3a66d9; }
+.filehooter div { border-top: 1px solid #ddd; }
+.hunkheader { background: rgb(255, 247, 212); color: #757575; }
+.lineno {
+  background: #fafafa;
+  box-sizing: border-box;
+  color: #666;
+  padding: 0 0.5em;
+  text-align: right;
+  vertical-align: top;
+  width: 94px;
+}
+.emptylineno { box-sizing: border-box; width: 94px; }
+.code { border-left: 1px solid #ddd; word-break: break-all; }
+.del { background: #ffeeee; }
+.del.strong { background: #ffcaca; }
+.add { background: #eeffee; }
+.add.strong { background: #caffca; }
+.binary { padding: 8px; border-left: 1px solid #ddd; }
+pre { white-space: pre-wrap; font-size: 14px; }
+</style>
+<body>
+"""
+
+
+def prettify_diff(diff_str):
+    diff_lines = diff_str.split('\n')
+    # List of DiffFile instances
+    diff_files = []
+
+    diff_file, diff_lines = DiffFile.parse(diff_lines)
+    while diff_file:
+        diff_files.append(diff_file)
+        diff_file, diff_lines = DiffFile.parse(diff_lines)
+
+    result_html = _LEADING_HTML
+    for diff_file in diff_files:
+        result_html += diff_file.prettify()
+
+    # If diff_lines still has unconsumed lines, this code has a bug or the input
+    # diff is broken. We show the raw diff in such case.
+    if diff_lines:
+        result_html += '<pre>'
+        for line in diff_lines:
+            result_html += cgi.escape(line) + '\n'
+        result_html += '</pre>'
+
+    return result_html + '</body>\n'
+
+
+class DiffFile(object):
+    """Represents diff for a single file.
+
+    An instance of this class contains one of the following:
+    - Text hunks
+    - Two binary hunks
+    - Meta information
+    """
+    LINK_BASE_URL = 'https://chromium.googlesource.com/chromium/src/+/master/'
+
+    def __init__(self, old_name, new_name, hunks=None, binaries=None, info=None):
+        assert old_name or new_name
+        assert bool(hunks) + bool(binaries) + bool(info) == 1
+        self._old_name = old_name
+        self._new_name = new_name
+        self._hunks = hunks
+        self._binaries = binaries
+        self._info = info
+
+    def prettify(self):
+        status = 'M'
+        pretty_name = self._linkify(self._new_name)
+        additional_info = ''
+        if self._old_name == '':
+            status = 'A'
+            pretty_name = cgi.escape(self._new_name)
+        elif self._new_name == '':
+            status = 'D'
+            pretty_name = self._linkify(self._old_name)
+        elif self._old_name != self._new_name:
+            status = 'R'
+            pretty_name = cgi.escape(self._new_name)
+            additional_info = ('\n<span class=rename>Renamed from {}</span>'
+                               .format(self._linkify(self._old_name)))
+
+        result_html = ('\n<table>\n<tr><td colspan=3 class=fileheader><div>' +
+                       status + ' ' + pretty_name + additional_info + '</div>')
+
+        if self._hunks:
+            for hunk in self._hunks:
+                result_html += hunk.prettify()
+        elif self._info:
+            result_html += '<tr><td colspan=3 class=fileinfo>{}</tr>'.format(
+                cgi.escape('\n'.join(self._info)))
+        else:
+            old_binary, new_binary = self._binaries  # pylint: disable=unpacking-non-sequence
+            if self._old_name and old_binary:
+                result_html += old_binary.prettify(self._mime_from_name(self._old_name), 'del')
+            if self._new_name and new_binary:
+                result_html += new_binary.prettify(self._mime_from_name(self._new_name), 'add')
+        return result_html + '<tr><td colspan=3 class=filehooter><div></div></table>\n'
+
+    def _linkify(self, name):
+        return '<a href="{url}" target="_new">{anchor}</a>'.format(
+            url=DiffFile.LINK_BASE_URL + cgi.escape(name), anchor=cgi.escape(name))
+
+    def _mime_from_name(self, name):
+        mime_type, _ = mimetypes.guess_type(name)
+        return mime_type if mime_type else 'application/octet-stream'
+
+    @staticmethod
+    def parse(lines):
+        """Parses diff lines, and creates a DiffFile instance.
+
+        Finds a file diff header, creates a single DiffFile instance, and
+        returns a tuple of the DiffFile instance and unconsumed lines. If a file
+        diff isn't found, (None, lines) is returned.
+        """
+        diff_command_re = r'diff (?:-[^ ]+ )*a/([^ ]+) b/([^ ]+)'
+        old_name = None
+        new_name = None
+        info_lines = None
+        found_diff_command_line = False
+        for i, line in enumerate(lines):
+            if not found_diff_command_line:
+                match = re.match(diff_command_re, line)
+                if not match:
+                    continue
+                old_name = match.group(1)
+                new_name = match.group(2)
+                info_lines = []
+                found_diff_command_line = True
+                continue
+
+            match = re.match(r'(GIT binary patch|--- ([^ ]+).*)', line)
+            if match:
+                if match.group(0) == 'GIT binary patch':
+                    return DiffFile._parse_binaries(lines[i + 1:], old_name, new_name)
+                return DiffFile._parse_text_hunks(lines[i:], old_name, new_name)
+
+            index_match = re.match(r'^index ([0-9a-f]+)\.\.([0-9a-f]+).*', line)
+            if index_match:
+                # Adjusts old_name and new_name for file addition/removal.
+                old_name, new_name = DiffFile._adjust_names(index_match, old_name, new_name)
+                continue
+
+            diff_match = re.match(diff_command_re, line)
+            if diff_match:
+                # There are no hunks. Renaming without any modification,
+                # or adding/removing an empty file.
+                return (DiffFile(old_name, new_name, info=info_lines), lines[i:])
+
+            # File mode, rename summary, etc.
+            info_lines.append(line)
+
+        if found_diff_command_line and info_lines:
+            return (DiffFile(old_name, new_name, info=info_lines), [])
+        return (None, lines)
+
+    @staticmethod
+    def _parse_binaries(lines, old_name, new_name):
+        new_binary, remaining_lines = BinaryHunk.parse(lines)
+        old_binary, remaining_lines = BinaryHunk.parse(remaining_lines)
+        return (DiffFile(old_name, new_name, binaries=(old_binary, new_binary)),
+                remaining_lines)
+
+    @staticmethod
+    def _parse_text_hunks(lines, old_name, new_name):
+        line = lines[0]
+        if len(lines) < 2:
+            raise ValueError('"+++ " line is missing after "{}"'.format(line))
+        next_line = lines[1]
+        if not next_line.startswith('+++ '):
+            raise ValueError('"+++ " line is missing after "{}"'.format(line))
+        hunks, remaining_lines = DiffHunk.parse(lines[2:])
+        return (DiffFile(old_name, new_name, hunks=hunks), remaining_lines)
+
+    @staticmethod
+    def _adjust_names(match, old_name, new_name):
+        old_index = match.group(1)
+        new_index = match.group(2)
+        if old_index and re.match(r'^0+$', old_index):
+            old_name = ''
+        if new_index and re.match(r'^0+$', new_index):
+            new_name = ''
+        return (old_name, new_name)
+
+
+class DiffHunk(object):
+    """Represents a single text hunk, starting with '@@ -d,d +d,d @@'.
+
+    This class also has code to detect character-level diff.
+    """
+
+    def __init__(self, old_start, new_start, context, lines):
+        self._old_start = old_start
+        self._new_start = new_start
+        self._context = ''
+        if context:
+            self._context = context
+            if self._context.startswith(' '):
+                self._context = self._context[1:]
+        self._lines = lines
+        # _annotations is a list of None or a list of tuples.
+        # A tuple consists of start index and end index, and it represents a
+        # modified part of a line, which should be highlighted in the pretty
+        # diff.
+        self._annotations = [None for _ in self._lines]
+        for deleted_index, inserted_index in self._find_operations(self._lines):
+            DiffHunk._annotate_character_diff(self._lines, deleted_index,
+                                              inserted_index, self._annotations)
+
+    @staticmethod
+    def _find_operations(lines):
+        """Finds 'operations' in the hunk.
+
+        A hunk contains one or more operations, and an operation is one of the
+        followings:
+          - Replace operation: '-' lines, followed by '+' lines
+          - Delete operation: '-' lines, not followed by '+' lines
+          - Insertion operation: '+' lines
+        """
+        # List of tuples which consist of (list of '-' line index, list of '+' line index)
+        operations = []
+        inserted_index = []
+        deleted_index = []
+        for i, line in enumerate(lines):
+            if line[0] == ' ':
+                if deleted_index or inserted_index:
+                    operations.append((deleted_index, inserted_index))
+                    deleted_index = []
+                    inserted_index = []
+            elif line[0] == '-':
+                if inserted_index:
+                    operations.append((deleted_index, inserted_index))
+                    deleted_index = []
+                    inserted_index = []
+                deleted_index.append(i)
+            else:
+                assert line[0] == '+'
+                inserted_index.append(i)
+        if deleted_index or inserted_index:
+            operations.append((deleted_index, inserted_index))
+        return operations
+
+    @staticmethod
+    def _annotate_character_diff(lines, deleted_index, inserted_index, annotations):
+        assert len(lines) == len(annotations)
+        if not deleted_index:
+            for i in inserted_index:
+                annotations[i] = [(0, len(lines[i]) - 1)]
+            return
+
+        if not inserted_index:
+            for i in deleted_index:
+                annotations[i] = [(0, len(lines[i]) - 1)]
+            return
+
+        deleted_str = ''.join([lines[i][1:] for i in deleted_index])
+        inserted_str = ''.join([lines[i][1:] for i in inserted_index])
+        matcher = difflib.SequenceMatcher(None, deleted_str, inserted_str)
+        for tag, d_start, d_end, i_start, i_end in matcher.get_opcodes():
+            if tag == 'delete':
+                DiffHunk._annotate(lines, deleted_index[0],
+                                   d_start, d_end, annotations)
+            elif tag == 'insert':
+                DiffHunk._annotate(lines, inserted_index[0],
+                                   i_start, i_end, annotations)
+            elif tag == 'replace':
+                DiffHunk._annotate(lines, deleted_index[0],
+                                   d_start, d_end, annotations)
+                DiffHunk._annotate(lines, inserted_index[0],
+                                   i_start, i_end, annotations)
+
+    @staticmethod
+    def _annotate(lines, index, start, end, annotations):
+        assert index < len(lines)
+        line_len = len(lines[index]) - 1
+        if line_len == 0 and start == 0:
+            annotations[index] = [(0, 0)]
+            DiffHunk._annotate(lines, index + 1, start, end, annotations)
+            return
+        if start >= line_len:
+            DiffHunk._annotate(lines, index + 1, start - line_len,
+                               end - line_len, annotations)
+            return
+        if not annotations[index]:
+            annotations[index] = []
+        annotations[index].append((start, min(line_len, end)))
+        if end > line_len:
+            DiffHunk._annotate(lines, index + 1, 0, end - line_len, annotations)
+
+    def prettify_code(self, index, klass):
+        line = self._lines[index][1:]
+        annotation = self._annotations[index]
+        if not annotation:
+            return '<td class="code {klass}">{code}'.format(
+                klass=klass, code=cgi.escape(line))
+
+        start, end = annotation[0]
+        if start == 0 and end == len(line):
+            return '<td class="code {klass} strong">{code}'.format(
+                klass=klass, code=cgi.escape(line))
+
+        i = 0
+        result_html = '<td class="code {}">'.format(klass)
+        for start, end in annotation:
+            result_html += cgi.escape(line[i:start])
+            result_html += '<span class="{} strong">'.format(klass)
+            result_html += cgi.escape(line[start:end])
+            result_html += '</span>'
+            i = end
+        return result_html + cgi.escape(line[i:])
+
+    def prettify(self):
+        result_html = ('<tr><td class=hunkheader>@@<td class=hunkheader>@@'
+                       '<td class=hunkheader>{}</tr>\n').format(self._context)
+        old_lineno = self._old_start
+        new_lineno = self._new_start
+        for i, line in enumerate(self._lines):
+            if line[0] == ' ':
+                result_html += ('<tr><td class=lineno>{old_lineno}<td '
+                                'class=lineno>{new_lineno}<td class=code>{code}'
+                                '</tr>\n').format(old_lineno=old_lineno,
+                                                  new_lineno=new_lineno,
+                                                  code=cgi.escape(line[1:]))
+                old_lineno += 1
+                new_lineno += 1
+            elif line[0] == '-':
+                result_html += '<tr><td class=lineno>{lineno}<td class=emptylineno>{code}</tr>\n'.format(
+                    lineno=old_lineno, code=self.prettify_code(i, 'del'))
+                old_lineno += 1
+            else:
+                assert line[0] == '+'
+                result_html += '<tr><td class=emptylineno><td class=lineno>{lineno}{code}</tr>\n'.format(
+                    lineno=new_lineno, code=self.prettify_code(i, 'add'))
+                new_lineno += 1
+        return result_html
+
+    @staticmethod
+    def parse(lines):
+        """Parses diff lines, and creates a sequence of DiffHunk instances.
+
+        Finds a hunk header, creates a sequence of DiffHunk instances, and
+        returns a tuple of the DiffHunk list and unconsumed lines. If a hunk
+        header isn't found, ValueError is raised.
+        """
+        old_start = None
+        new_start = None
+        context = None
+        hunk_lines = None
+        hunks = []
+        hunk_header_re = r'^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)?'
+        found_hunk_header = False
+        for i, line in enumerate(lines):
+            if not found_hunk_header:
+                match = re.match(hunk_header_re, line)
+                if match:
+                    found_hunk_header = True
+                    old_start = int(match.group(1))
+                    new_start = int(match.group(2))
+                    context = match.group(3)
+                    hunk_lines = []
+                continue
+            if line.startswith((' ', '-', '+')):
+                hunk_lines.append(line)
+                continue
+            hunks.append(DiffHunk(old_start, new_start, context, hunk_lines))
+            match = re.match(hunk_header_re, line)
+            if not match:
+                return (hunks, lines[i:])
+            old_start = int(match.group(1))
+            new_start = int(match.group(2))
+            context = match.group(3)
+            hunk_lines = []
+        if found_hunk_header:
+            hunks.append(DiffHunk(old_start, new_start, context, hunk_lines))
+        else:
+            raise ValueError('Found no hunks')
+        return (hunks, [])
+
+
+class BinaryHunk(object):
+    """Represents a binary hunk.
+
+    A binary diff for a single file contains two binary hunks. An
+    instance of this class represents a single binary hunk.
+    """
+
+    def __init__(self, bin_type, size, bin_data):
+        assert bin_type in ('literal', 'delta')
+        self._type = bin_type
+        self._size = size
+        self._compressed_data = bin_data
+
+    def prettify(self, mime_type, klass):
+        result_html = ('<tr><td class=emptylineno><td class=emptylineno>'
+                       '<td class="{klass} strong binary">Binary {type}; {size}'
+                       ' Bytes<br>\n').format(klass=klass, type=self._type,
+                                              size=self._size)
+        if self._type == 'delta':
+            # Because we can assume the input diff is always produced by git, we
+            # can obtain the original blob, apply the delta, and render both of
+            # the original blob and the patched blob. However, we're not sure
+            # how much it is worth to do.
+            #
+            # For 'delta' format, see patch_delta() in patch-delta.c.
+            # https://github.com/git/git/blob/master/patch-delta.c
+            return result_html + 'We don\'t support rendering a delta binary hunk.'
+        if mime_type.startswith('image/'):
+            return result_html + '<img src="data:{type};base64,{data}">'.format(
+                type=mime_type, data=base64.b64encode(zlib.decompress(self._compressed_data)))
+        return result_html + 'We don\'t support rendering {} binary.'.format(mime_type)
+
+    @staticmethod
+    def parse(lines):
+        """Creates a BinaryHunk instance starting with lines[0].
+
+        Returns a tuple of the BinaryHunk instance and unconsumed lines.
+        """
+        match = re.match(r'(literal|delta) (\d+)', lines[0])
+        if not match:
+            raise ValueError('No "literal <size>" or "delta <size>".')
+        bin_type = match.group(1)
+        size = int(match.group(2))
+        bin_data = ''
+
+        lines = lines[1:]
+        for i, line in enumerate(lines):
+            if len(line) == 0:
+                return (BinaryHunk(bin_type, size, bin_data), lines[i + 1:])
+            line_length_letter = line[0]
+            # Map a letter to a number.
+            #   A-Z -> 1-26
+            #   a-z -> 27-52
+            line_length = 1 + ord(line_length_letter) - ord('A')
+            if line_length_letter >= 'a':
+                line_length = 27 + ord(line_length_letter) - ord('a')
+            if line_length * 5 > (len(line) - 1) * 4:
+                raise ValueError('Base85 length mismatch: length by the first '
+                                 'letter:{}, actual:{}, line:"{}"'.format(
+                                     line_length * 5, (len(line) - 1) * 4, line))
+            bin_data += decode_base85(line[1:])[0:line_length]
+        raise ValueError('No blank line terminating a binary hunk.')
diff --git a/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py b/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py
new file mode 100644
index 0000000..3aae9fd
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py
@@ -0,0 +1,194 @@
+# 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 re
+import unittest
+
+from blinkpy.common.pretty_diff import BinaryHunk, DiffFile, DiffHunk
+
+
+# This test contains tests for protected methods.
+# pylint: disable=protected-access
+
+class TestFileDiff(unittest.TestCase):
+
+    def _assert_file_status(self, diff, status):
+        html = diff.prettify()
+        match = re.search(r'\b([A-Z]) ', html)
+        self.assertTrue(match and match.group(1) == status)
+
+    def test_empty_input(self):
+        lines = []
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNone(diff)
+        self.assertEquals(remaining_lines, [])
+
+    def test_100percent_similarity(self):
+        # crrev.com/c576df77d72abe47154ff2489bb035aa20892f7f
+        lines = ['diff --git a/platform/modules/offscreencanvas/OWNERS b/platform/modules/frame_sinks/OWNERS',
+                 'similarity index 100%',
+                 'rename from platform/modules/offscreencanvas/OWNERS',
+                 'rename to platform/modules/frame_sinks/OWNERS',
+                 'diff --git a/platform/modules/frame_sinks/embedded_frame_sink.mojom ' +
+                 'b/platform/modules/frame_sinks/embedded_frame_sink.mojom']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines[0], lines[4])
+
+    def test_emptify_text(self):
+        lines = ['diff --git a/third_party/blink/text-to-zero.txt b/third_party/blink/text-to-zero.txt',
+                 'index 2262de0..e69de29 100644',
+                 '--- a/third_party/blink/text-to-zero.txt',
+                 '+++ b/third_party/blink/text-to-zero.txt',
+                 '@@ -1 +0,0 @@',
+                 '-hoge']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'M')
+
+    def test_remove_text(self):
+        lines = ['diff --git a/text-to-be-removed.txt b/text-to-be-removed.txt',
+                 'deleted file mode 100644',
+                 'index 2262de0..0000000',
+                 '--- a/text-to-be-removed.txt',
+                 '+++ /dev/null',
+                 '@@ -1 +0,0 @@',
+                 '-hoge']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'D')
+
+    def test_remove_zero_byte_text(self):
+        lines = ['diff --git a/text-zero.txt b/text-zero.txt',
+                 'deleted file mode 100644',
+                 'index e69de29..0000000']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'D')
+
+    def test_add_empty_text(self):
+        lines = ['diff --git a/text-zero.txt b/text-zero.txt',
+                 'new file mode 100644',
+                 'index 0000000..e69de29']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'A')
+
+    def test_emptify_binary(self):
+        lines = ['diff --git a/binary-to-zero.png b/binary-to-zero.png',
+                 'index 9b56f1c6942441578b0585d8b9688fdfcb2aa3fd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644',
+                 'GIT binary patch',
+                 'literal 0',
+                 'HcmV?d00001',
+                 '',
+                 'literal 6',
+                 'NcmZSh&&2%iKL7{~0|Ed5',
+                 '']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'M')
+
+    def test_remove_binary(self):
+        lines = ['diff --git a/binary-to-be-removed.png b/binary-to-be-removed.png',
+                 'deleted file mode 100644',
+                 'index 9b56f1c6942441578b0585d8b9688fdfcb2aa3fd..0000000000000000000000000000000000000000',
+                 'GIT binary patch',
+                 'literal 0',
+                 'HcmV?d00001',
+                 '',
+                 'literal 6',
+                 'NcmZSh&&2%iKL7{~0|Ed5',
+                 '']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'D')
+
+    def test_add_binary(self):
+        lines = ['diff --git a/binary-to-zero.png b/binary-to-zero.png',
+                 'new file mode 100644',
+                 'index 0000000000000000000000000000000000000000..9b56f1c6942441578b0585d8b9688fdfcb2aa3fd',
+                 'GIT binary patch',
+                 'literal 6',
+                 'NcmZSh&&2%iKL7{~0|Ed5',
+                 '',
+                 'literal 0',
+                 'HcmV?d00001',
+                 '']
+        diff, remaining_lines = DiffFile.parse(lines)
+        self.assertIsNotNone(diff)
+        self.assertEquals(remaining_lines, [])
+        self._assert_file_status(diff, 'A')
+
+
+class TestDiffHunk(unittest.TestCase):
+
+    def test_find_operations(self):
+        self.assertEquals(DiffHunk._find_operations([]), [])
+        self.assertEquals(DiffHunk._find_operations([' ']), [])
+
+        self.assertEquals(DiffHunk._find_operations(['-']), [([0], [])])
+        self.assertEquals(DiffHunk._find_operations(['-', '-']), [([0, 1], [])])
+        self.assertEquals(DiffHunk._find_operations([' ', '-', '-']),
+                          [([1, 2], [])])
+        self.assertEquals(DiffHunk._find_operations(['-', '-', ' ']),
+                          [([0, 1], [])])
+
+        self.assertEquals(DiffHunk._find_operations(['+']), [([], [0])])
+        self.assertEquals(DiffHunk._find_operations(['+', '+']), [([], [0, 1])])
+        self.assertEquals(DiffHunk._find_operations([' ', '+', '+']),
+                          [([], [1, 2])])
+        self.assertEquals(DiffHunk._find_operations(['+', '+', ' ']),
+                          [([], [0, 1])])
+
+        self.assertEquals(DiffHunk._find_operations(['-', '+']), [([0], [1])])
+        self.assertEquals(DiffHunk._find_operations(['-', '-', '+', '+']),
+                          [([0, 1], [2, 3])])
+        self.assertEquals(DiffHunk._find_operations([' ', '-', '-', '+']),
+                          [([1, 2], [3])])
+        self.assertEquals(DiffHunk._find_operations(['-', '-', '+', '+', ' ']),
+                          [([0, 1], [2, 3])])
+        self.assertEquals(DiffHunk._find_operations(['-', '-', '+', '+', '-']),
+                          [([0, 1], [2, 3]), ([4], [])])
+        self.assertEquals(DiffHunk._find_operations(['-', '+', '-', '+']),
+                          [([0], [1]), ([2], [3])])
+
+    def _annotate(self, lines, index, start, end):
+        annotations = [None for _ in lines]
+        DiffHunk._annotate(lines, index, start, end, annotations)
+        return annotations
+
+    def test_annotate(self):
+        self.assertEquals(self._annotate(['-abcdef'], 0, 2, 4),
+                          [[(2, 4)]])
+        self.assertEquals(self._annotate(['-abcdef', '-ghi'], 0, 2, 6),
+                          [[(2, 6)], None])
+        self.assertEquals(self._annotate(['-abcdef', '-ghi'], 0, 2, 7),
+                          [[(2, 6)], [(0, 1)]])
+        self.assertEquals(self._annotate(['-abcdef', '-ghi', '-jkl'], 0, 2, 11),
+                          [[(2, 6)], [(0, 3)], [(0, 2)]])
+        self.assertEquals(self._annotate(['+', '+abc', ' de'], 0, 0, 2),
+                          [[(0, 0)], [(0, 2)], None])
+
+
+class TestBinaryHunk(unittest.TestCase):
+
+    def test_literal_image(self):
+        lines = ['literal 6', 'NcmZSh&&2%iKL7{~0|Ed5', '', 'literal 0...']
+        binary, remaining_lines = BinaryHunk.parse(lines)
+        self.assertIsNotNone(binary)
+        self.assertEquals(remaining_lines[0], lines[3])
+        self.assertTrue('data:image/png;base64,' in binary.prettify('image/png', 'add'))
+
+    def test_literal_non_image(self):
+        lines = ['literal 6', 'NcmZSh&&2%iKL7{~0|Ed5', '']
+        binary, remaining_lines = BinaryHunk.parse(lines)
+        self.assertIsNotNone(binary)
+        self.assertEquals(remaining_lines, [])
+        self.assertTrue('<img ' not in binary.prettify('application/octet-stream', 'del'))
diff --git a/third_party/blink/tools/blinkpy/common/prettypatch.py b/third_party/blink/tools/blinkpy/common/prettypatch.py
deleted file mode 100644
index 1c61077..0000000
--- a/third_party/blink/tools/blinkpy/common/prettypatch.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (c) 2010 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import os
-import tempfile
-
-
-class PrettyPatch(object):
-
-    def __init__(self, executive):
-        self._executive = executive
-
-    def pretty_diff_file(self, diff):
-        # Diffs can contain multiple text files of different encodings
-        # so we always deal with them as byte arrays, not unicode strings.
-        assert isinstance(diff, str)
-        pretty_diff = self.pretty_diff(diff)
-        diff_file = tempfile.NamedTemporaryFile(suffix='.html')
-        diff_file.write(pretty_diff)
-        diff_file.flush()
-        return diff_file
-
-    def pretty_diff(self, diff):
-        # prettify.rb will hang forever if given no input.
-        # Avoid the hang by returning an empty string.
-        if not diff:
-            return ''
-
-        pretty_patch_path = os.path.join(os.path.dirname(__file__), '..', '..',
-                                         'blinkruby')
-        prettify_path = os.path.join(pretty_patch_path, 'prettify.rb')
-        args = [
-            'ruby',
-            '-I',
-            pretty_patch_path,
-            prettify_path,
-        ]
-        # PrettyPatch does not modify the encoding of the diff output
-        # so we can't expect it to be utf-8.
-        return self._executive.run_command(args, input=diff, decode_output=False)
diff --git a/third_party/blink/tools/blinkpy/tool/commands/pretty_diff.py b/third_party/blink/tools/blinkpy/tool/commands/pretty_diff.py
index 5236aa03..54c22121 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/pretty_diff.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/pretty_diff.py
@@ -29,9 +29,10 @@
 import logging
 import optparse
 import sys
+import tempfile
 import urllib
 
-from blinkpy.common.prettypatch import PrettyPatch
+from blinkpy.common.pretty_diff import prettify_diff
 from blinkpy.common.system.executive import ScriptError
 from blinkpy.tool.commands.command import Command
 
@@ -66,9 +67,8 @@
         if not self._tool.user.can_open_url():
             return None
         try:
-            pretty_patch = PrettyPatch(self._tool.executive)
             patch = self._diff(options)
-            pretty_diff_file = pretty_patch.pretty_diff_file(patch)
+            pretty_diff_file = PrettyDiff._pretty_diff_file(patch)
             self._open_pretty_diff(pretty_diff_file.name)
             # We return the pretty_diff_file here because we need to keep the
             # file alive until the user has had a chance to confirm the diff.
@@ -85,6 +85,16 @@
         return self._tool.git().create_patch(options.git_commit,
                                              changed_files=changed_files)
 
+    @staticmethod
+    def _pretty_diff_file(diff):
+        # Diffs can contain multiple text files of different encodings
+        # so we always deal with them as byte arrays, not unicode strings.
+        assert isinstance(diff, str)
+        diff_file = tempfile.NamedTemporaryFile(suffix='.html')
+        diff_file.write(prettify_diff(diff))
+        diff_file.flush()
+        return diff_file
+
     def _open_pretty_diff(self, file_path):
         url = 'file://%s' % urllib.quote(file_path)
         self._tool.user.open_url(url)
diff --git a/third_party/blink/tools/blinkruby/PrettyPatch.rb b/third_party/blink/tools/blinkruby/PrettyPatch.rb
deleted file mode 100644
index 3345cbf..0000000
--- a/third_party/blink/tools/blinkruby/PrettyPatch.rb
+++ /dev/null
@@ -1,980 +0,0 @@
-require 'cgi'
-require 'diff'
-require 'open3'
-require 'open-uri'
-require 'pp'
-require 'set'
-require 'tempfile'
-
-module PrettyPatch
-
-public
-
-    GIT_PATH = "git"
-
-    def self.prettify(string)
-        $last_prettify_file_count = -1
-        $last_prettify_part_count = { "remove" => 0, "add" => 0, "shared" => 0, "binary" => 0, "extract-error" => 0 }
-        string = normalize_line_ending(string)
-        str = "#{HEADER}<body>\n"
-
-        # Just look at the first line to see if it is an SVN revision number as added
-        # by blink_tool.py for git checkouts.
-        $svn_revision = 0
-        string.each_line do |line|
-            match = /^Subversion\ Revision: (\d*)$/.match(line)
-            unless match.nil?
-                str << "<span class='revision'>#{match[1]}</span>\n"
-                $svn_revision = match[1].to_i;
-            end
-            break
-        end
-
-        fileDiffs = FileDiff.parse(string)
-
-        $last_prettify_file_count = fileDiffs.length
-        str << fileDiffs.collect{ |diff| diff.to_html }.join
-        str << "</body></html>"
-    end
-
-    def self.filename_from_diff_header(line)
-        DIFF_HEADER_FORMATS.each do |format|
-            match = format.match(line)
-            return match[1] unless match.nil?
-        end
-        nil
-    end
-
-    def self.diff_header?(line)
-        RELAXED_DIFF_HEADER_FORMATS.any? { |format| line =~ format }
-    end
-
-private
-    DIFF_HEADER_FORMATS = [
-        /^Index: (.*)\r?$/,
-        /^diff --git "?a\/.+"? "?b\/(.+)"?\r?$/,
-        /^\+\+\+ ([^\t]+)(\t.*)?\r?$/
-    ]
-
-    RELAXED_DIFF_HEADER_FORMATS = [
-        /^Index:/,
-        /^diff/
-    ]
-
-    BINARY_FILE_MARKER_FORMAT = /^Cannot display: file marked as a binary type.$/
-
-    IMAGE_FILE_MARKER_FORMAT = /^svn:mime-type = image\/png$/
-
-    GIT_INDEX_MARKER_FORMAT = /^index ([0-9a-f]{40})\.\.([0-9a-f]{40})/
-
-    GIT_BINARY_FILE_MARKER_FORMAT = /^GIT binary patch$/
-
-    GIT_BINARY_PATCH_FORMAT = /^(literal|delta) \d+$/
-
-    GIT_LITERAL_FORMAT = /^literal \d+$/
-
-    GIT_DELTA_FORMAT = /^delta \d+$/
-
-    START_OF_BINARY_DATA_FORMAT = /^[0-9a-zA-Z\+\/=]{20,}/ # Assume 20 chars without a space is base64 binary data.
-
-    START_OF_SECTION_FORMAT = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@\s*(.*)/
-
-    START_OF_EXTENT_STRING = "%c" % 0
-    END_OF_EXTENT_STRING = "%c" % 1
-
-    # We won't search for intra-line diffs in lines longer than this length, to avoid hangs. See <http://webkit.org/b/56109>.
-    MAXIMUM_INTRALINE_DIFF_LINE_LENGTH = 10000
-
-    SMALLEST_EQUAL_OPERATION = 3
-
-    OPENSOURCE_URL = "https://chromium.googlesource.com/chromium/src/+/master/"
-
-    IMAGE_CHECKSUM_ERROR = "INVALID: Image lacks a checksum. This will fail with a MISSING error in run_web_tests.py. Always generate new png files using run_web_tests.py."
-
-    def self.normalize_line_ending(s)
-        if RUBY_VERSION >= "1.9"
-            # Transliteration table from http://stackoverflow.com/a/6609998
-            transliteration_table = { '\xc2\x82' => ',',        # High code comma
-                                      '\xc2\x84' => ',,',       # High code double comma
-                                      '\xc2\x85' => '...',      # Tripple dot
-                                      '\xc2\x88' => '^',        # High carat
-                                      '\xc2\x91' => '\x27',     # Forward single quote
-                                      '\xc2\x92' => '\x27',     # Reverse single quote
-                                      '\xc2\x93' => '\x22',     # Forward double quote
-                                      '\xc2\x94' => '\x22',     # Reverse double quote
-                                      '\xc2\x95' => ' ',
-                                      '\xc2\x96' => '-',        # High hyphen
-                                      '\xc2\x97' => '--',       # Double hyphen
-                                      '\xc2\x99' => ' ',
-                                      '\xc2\xa0' => ' ',
-                                      '\xc2\xa6' => '|',        # Split vertical bar
-                                      '\xc2\xab' => '<<',       # Double less than
-                                      '\xc2\xbb' => '>>',       # Double greater than
-                                      '\xc2\xbc' => '1/4',      # one quarter
-                                      '\xc2\xbd' => '1/2',      # one half
-                                      '\xc2\xbe' => '3/4',      # three quarters
-                                      '\xca\xbf' => '\x27',     # c-single quote
-                                      '\xcc\xa8' => '',         # modifier - under curve
-                                      '\xcc\xb1' => ''          # modifier - under line
-                                   }
-            encoded_string = s.force_encoding('UTF-8').encode('UTF-16', :invalid => :replace, :replace => '', :fallback => transliteration_table).encode('UTF-8')
-            encoded_string.gsub /\r\n?/, "\n"
-        else
-            s.gsub /\r\n?/, "\n"
-        end
-    end
-
-    def self.linkifyFilename(filename)
-        "<a href='#{OPENSOURCE_URL}#{filename}'>#{filename}</a>"
-    end
-
-
-    HEADER =<<EOF
-<html>
-<head>
-<style>
-:link, :visited {
-    text-decoration: none;
-    border-bottom: 1px dotted;
-}
-
-:link {
-    color: #039;
-}
-
-.FileDiff {
-    background-color: #f8f8f8;
-    border: 1px solid #ddd;
-    font-family: monospace;
-    margin: 1em 0;
-    position: relative;
-}
-
-h1 {
-    color: #333;
-    font-family: sans-serif;
-    font-size: 1em;
-    margin-left: 0.5em;
-    display: table-cell;
-    width: 100%;
-    padding: 0.5em;
-}
-
-h1 :link, h1 :visited {
-    color: inherit;
-}
-
-h1 :hover {
-    color: #555;
-    background-color: #eee;
-}
-
-.DiffLinks {
-    float: right;
-}
-
-.FileDiffLinkContainer {
-    opacity: 0;
-    display: table-cell;
-    padding-right: 0.5em;
-    white-space: nowrap;
-}
-
-.DiffSection {
-    background-color: white;
-    border: solid #ddd;
-    border-width: 1px 0px;
-}
-
-.ExpansionLine, .LineContainer {
-    white-space: nowrap;
-}
-
-.sidebyside .DiffBlockPart.add:first-child {
-    float: right;
-}
-
-.LineSide,
-.sidebyside .DiffBlockPart.remove,
-.sidebyside .DiffBlockPart.add {
-    display:inline-block;
-    width: 50%;
-    vertical-align: top;
-}
-
-.sidebyside .resizeHandle {
-    width: 5px;
-    height: 100%;
-    cursor: move;
-    position: absolute;
-    top: 0;
-    left: 50%;
-}
-
-.sidebyside .resizeHandle:hover {
-    background-color: grey;
-    opacity: 0.5;
-}
-
-.sidebyside .DiffBlockPart.remove .to,
-.sidebyside .DiffBlockPart.add .from {
-    display: none;
-}
-
-.lineNumber, .expansionLineNumber {
-    border-bottom: 1px solid #998;
-    border-right: 1px solid #ddd;
-    color: #444;
-    display: inline-block;
-    padding: 1px 5px 0px 0px;
-    text-align: right;
-    vertical-align: bottom;
-    width: 3em;
-}
-
-.lineNumber {
-  background-color: #eed;
-}
-
-.expansionLineNumber {
-  background-color: #eee;
-}
-
-.text {
-    padding-left: 5px;
-    white-space: pre-wrap;
-    word-wrap: break-word;
-}
-
-.image {
-    border: 2px solid black;
-}
-
-.context, .context .lineNumber {
-    color: #849;
-    background-color: #fef;
-}
-
-.Line.add, .FileDiff .add {
-    background-color: #dfd;
-}
-
-.Line.add ins {
-    background-color: #9e9;
-    text-decoration: none;
-}
-
-.Line.remove, .FileDiff .remove {
-    background-color: #fdd;
-}
-
-.Line.remove del {
-    background-color: #e99;
-    text-decoration: none;
-}
-
-/* Support for inline comments */
-
-.author {
-  font-style: italic;
-}
-
-.comment {
-  position: relative;
-}
-
-.comment textarea {
-  height: 6em;
-}
-
-.overallComments textarea {
-  height: 2em;
-  max-width: 100%;
-  min-width: 200px;
-}
-
-.comment textarea, .overallComments textarea {
-  display: block;
-  width: 100%;
-}
-
-.overallComments .open {
-  -webkit-transition: height .2s;
-  height: 4em;
-}
-
-#statusBubbleContainer.wrap {
-  display: block;
-}
-
-#toolbar {
-  display: -webkit-flex;
-  display: -moz-flex;
-  padding: 3px;
-  left: 0;
-  right: 0;
-  border: 1px solid #ddd;
-  background-color: #eee;
-  font-family: sans-serif;
-  position: fixed;
-  bottom: 0;
-}
-
-#toolbar .actions {
-  float: right;
-}
-
-.winter {
-  position: fixed;
-  z-index: 5;
-  left: 0;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  background-color: black;
-  opacity: 0.8;
-}
-
-.inactive {
-  display: none;
-}
-
-.lightbox {
-  position: fixed;
-  z-index: 6;
-  left: 10%;
-  right: 10%;
-  top: 10%;
-  bottom: 10%;
-  background: white;
-}
-
-.lightbox iframe {
-  width: 100%;
-  height: 100%;
-}
-
-.commentContext .lineNumber {
-  background-color: yellow;
-}
-
-.selected .lineNumber {
-  background-color: #69F;
-  border-bottom-color: #69F;
-  border-right-color: #69F;
-}
-
-.ExpandLinkContainer {
-  opacity: 0;
-  border-top: 1px solid #ddd;
-  border-bottom: 1px solid #ddd;
-}
-
-.ExpandArea {
-  margin: 0;
-}
-
-.ExpandText {
-  margin-left: 0.67em;
-}
-
-.LinkContainer {
-  font-family: sans-serif;
-  font-size: small;
-  font-style: normal;
-  -webkit-transition: opacity 0.5s;
-}
-
-.LinkContainer a {
-  border: 0;
-}
-
-.LinkContainer label:after,
-.LinkContainer a:after {
-  content: " | ";
-  color: black;
-}
-
-.LinkContainer a:last-of-type:after {
-  content: "";
-}
-
-.LinkContainer label {
-  color: #039;
-}
-
-.help {
- color: gray;
- font-style: italic;
-}
-
-#message {
-  font-size: small;
-  font-family: sans-serif;
-}
-
-.commentStatus {
-  font-style: italic;
-}
-
-.comment, .previousComment, .frozenComment {
-  background-color: #ffd;
-}
-
-.overallComments {
-  -webkit-flex: 1;
-  -moz-flex: 1;
-  margin-right: 3px;
-}
-
-.previousComment, .frozenComment {
-  border: inset 1px;
-  padding: 5px;
-  white-space: pre-wrap;
-}
-
-.comment button {
-  width: 6em;
-}
-
-div:focus {
-  outline: 1px solid blue;
-  outline-offset: -1px;
-}
-
-.statusBubble {
-  /* The width/height get set to the bubble contents via postMessage on browsers that support it. */
-  width: 450px;
-  height: 20px;
-  margin: 2px 2px 0 0;
-  border: none;
-  vertical-align: middle;
-}
-
-.revision {
-  display: none;
-}
-
-.autosave-state {
-  position: absolute;
-  right: 0;
-  top: -1.3em;
-  padding: 0 3px;
-  outline: 1px solid #DDD;
-  color: #8FDF5F;
-  font-size: small;   
-  background-color: #EEE;
-}
-
-.autosave-state:empty {
-  outline: 0px;
-}
-.autosave-state.saving {
-  color: #E98080;
-}
-
-.clear_float {
-    clear: both;
-}
-</style>
-</head>
-EOF
-
-    def self.revisionOrDescription(string)
-        case string
-        when /\(revision \d+\)/
-            /\(revision (\d+)\)/.match(string)[1]
-        when /\(.*\)/
-            /\((.*)\)/.match(string)[1]
-        end
-    end
-
-    def self.has_image_suffix(filename)
-        filename =~ /\.(png|jpg|gif)$/
-    end
-
-    class FileDiff
-        def initialize(lines)
-            @filename = PrettyPatch.filename_from_diff_header(lines[0].chomp)
-            startOfSections = 1
-            for i in 0...lines.length
-                case lines[i]
-                when /^--- /
-                    @from = PrettyPatch.revisionOrDescription(lines[i])
-                when /^\+\+\+ /
-                    @filename = PrettyPatch.filename_from_diff_header(lines[i].chomp) if @filename.nil?
-                    @to = PrettyPatch.revisionOrDescription(lines[i])
-                    startOfSections = i + 1
-                    break
-                when BINARY_FILE_MARKER_FORMAT
-                    @binary = true
-                    if (IMAGE_FILE_MARKER_FORMAT.match(lines[i + 1]) or PrettyPatch.has_image_suffix(@filename)) then
-                        @image = true
-                        startOfSections = i + 2
-                        for x in startOfSections...lines.length
-                            # Binary diffs often have property changes listed before the actual binary data.  Skip them.
-                            if START_OF_BINARY_DATA_FORMAT.match(lines[x]) then
-                                startOfSections = x
-                                break
-                            end
-                        end
-                    end
-                    break
-                when GIT_INDEX_MARKER_FORMAT
-                    @git_indexes = [$1, $2]
-                when GIT_BINARY_FILE_MARKER_FORMAT
-                    @binary = true
-                    if (GIT_BINARY_PATCH_FORMAT.match(lines[i + 1]) and PrettyPatch.has_image_suffix(@filename)) then
-                        @git_image = true
-                        startOfSections = i + 1
-                    end
-                    break
-                end
-            end
-            lines_with_contents = lines[startOfSections...lines.length]
-            @sections = DiffSection.parse(lines_with_contents) unless @binary
-            if @image and not lines_with_contents.empty?
-                @image_url = "data:image/png;base64," + lines_with_contents.join
-                @image_checksum = FileDiff.read_checksum_from_png(lines_with_contents.join.unpack("m").join)
-            elsif @git_image
-                begin
-                    raise "index line is missing" unless @git_indexes
-
-                    chunks = nil
-                    for i in 0...lines_with_contents.length
-                        if lines_with_contents[i] =~ /^$/
-                            chunks = [lines_with_contents[i + 1 .. -1], lines_with_contents[0 .. i]]
-                            break
-                        end
-                    end
-
-                    raise "no binary chunks" unless chunks
-
-                    from_filepath = FileDiff.extract_contents_of_from_revision(@filename, chunks[0], @git_indexes[0])
-                    to_filepath = FileDiff.extract_contents_of_to_revision(@filename, chunks[1], @git_indexes[1], from_filepath, @git_indexes[0])
-                    filepaths = from_filepath, to_filepath
-
-                    binary_contents = filepaths.collect { |filepath| File.exists?(filepath) ? File.read(filepath) : nil }
-                    @image_urls = binary_contents.collect { |content| (content and not content.empty?) ? "data:image/png;base64," + [content].pack("m") : nil }
-                    @image_checksums = binary_contents.collect { |content| FileDiff.read_checksum_from_png(content) }
-                rescue
-                    $last_prettify_part_count["extract-error"] += 1
-                    @image_error = "Exception raised during decoding git binary patch:<pre>#{CGI.escapeHTML($!.to_s + "\n" + $!.backtrace.join("\n"))}</pre>"
-                ensure
-                    File.unlink(from_filepath) if (from_filepath and File.exists?(from_filepath))
-                    File.unlink(to_filepath) if (to_filepath and File.exists?(to_filepath))
-                end
-            end
-            nil
-        end
-
-        def image_to_html
-            if not @image_url then
-                return "<span class='text'>Image file removed</span>"
-            end
-
-            image_checksum = ""
-            if @image_checksum
-                image_checksum = @image_checksum
-            elsif @filename.include? "-expected.png" and @image_url
-                image_checksum = IMAGE_CHECKSUM_ERROR
-            end
-
-            return "<p>" + image_checksum + "</p><img class='image' src='" + @image_url + "' />"
-        end
-
-        def to_html
-            str = "<div class='FileDiff'>\n"
-            str += "<h1>#{PrettyPatch.linkifyFilename(@filename)}</h1>\n"
-            if @image then
-                str += self.image_to_html
-            elsif @git_image then
-                if @image_error
-                    str += @image_error
-                else
-                    for i in (0...2)
-                        image_url = @image_urls[i]
-                        image_checksum = @image_checksums[i]
-
-                        style = ["remove", "add"][i]
-                        str += "<p class=\"#{style}\">"
-
-                        if image_checksum
-                            str += image_checksum
-                        elsif @filename.include? "-expected.png" and image_url
-                            str += IMAGE_CHECKSUM_ERROR
-                        end
-
-                        str += "<br>"
-
-                        if image_url
-                            str += "<img class='image' src='" + image_url + "' />"
-                        else
-                            str += ["</p>Added", "</p>Removed"][i]
-                        end
-                    end
-                end
-            elsif @binary then
-                $last_prettify_part_count["binary"] += 1
-                str += "<span class='text'>Binary file, nothing to see here</span>"
-            else
-                str += @sections.collect{ |section| section.to_html }.join("<br>\n") unless @sections.nil?
-            end
-
-            if @from then
-                str += "<span class='revision'>" + @from + "</span>"
-            end
-
-            str += "</div>\n"
-        end
-
-        def self.parse(string)
-            haveSeenDiffHeader = false
-            linesForDiffs = []
-            string.each_line do |line|
-                if (PrettyPatch.diff_header?(line))
-                    linesForDiffs << []
-                    haveSeenDiffHeader = true
-                elsif (!haveSeenDiffHeader && line =~ /^--- /)
-                    linesForDiffs << []
-                    haveSeenDiffHeader = false
-                end
-                linesForDiffs.last << line unless linesForDiffs.last.nil?
-            end
-
-            linesForDiffs.collect { |lines| FileDiff.new(lines) }
-        end
-
-        def self.read_checksum_from_png(png_bytes)
-            # Ruby 1.9 added the concept of string encodings, so to avoid treating binary data as UTF-8,
-            # we can force the encoding to binary at this point.
-            if RUBY_VERSION >= "1.9"
-                png_bytes.force_encoding('binary')
-            end
-            match = png_bytes && png_bytes.match(/tEXtchecksum\0([a-fA-F0-9]{32})/)
-            match ? match[1] : nil
-        end
-
-        def self.git_new_file_binary_patch(filename, encoded_chunk, git_index)
-            return <<END
-diff --git a/#{filename} b/#{filename}
-new file mode 100644
-index 0000000000000000000000000000000000000000..#{git_index}
-GIT binary patch
-#{encoded_chunk.join("")}literal 0
-HcmV?d00001
-
-END
-        end
-
-        def self.git_changed_file_binary_patch(to_filename, from_filename, encoded_chunk, to_git_index, from_git_index)
-            return <<END
-diff --git a/#{from_filename} b/#{to_filename}
-copy from #{from_filename}
-+++ b/#{to_filename}
-index #{from_git_index}..#{to_git_index}
-GIT binary patch
-#{encoded_chunk.join("")}literal 0
-HcmV?d00001
-
-END
-        end
-
-        def self.get_svn_uri(repository_path)
-            "http://src.chromium.org/blink/trunk/" + (repository_path) + "?p=" + $svn_revision.to_s
-        end
-
-        def self.get_new_temp_filepath_and_name
-            tempfile = Tempfile.new("PrettyPatch")
-            filepath = tempfile.path + '.bin'
-            filename = File.basename(filepath)
-            return filepath, filename
-        end
-
-        def self.download_from_revision_from_svn(repository_path)
-            filepath, filename = get_new_temp_filepath_and_name
-            svn_uri = get_svn_uri(repository_path)
-            open(filepath, 'wb') do |to_file|
-                to_file << open(svn_uri) { |from_file| from_file.read }
-            end
-            return filepath
-        end
-
-        def self.run_git_apply_on_patch(output_filepath, patch)
-            # Apply the git binary patch using git-apply.
-            cmd = GIT_PATH + " apply --directory=" + File.dirname(output_filepath)
-            stdin, stdout, stderr = *Open3.popen3(cmd)
-            begin
-                stdin.puts(patch)
-                stdin.close
-
-                error = stderr.read
-                if error != ""
-                    error = "Error running " + cmd + "\n" + "with patch:\n" + patch[0..500] + "...\n" + error
-                end
-                raise error if error != ""
-            ensure
-                stdin.close unless stdin.closed?
-                stdout.close
-                stderr.close
-            end
-        end
-
-        def self.extract_contents_from_git_binary_literal_chunk(encoded_chunk, git_index)
-            filepath, filename = get_new_temp_filepath_and_name
-            patch = FileDiff.git_new_file_binary_patch(filename, encoded_chunk, git_index)
-            run_git_apply_on_patch(filepath, patch)
-            return filepath
-        end
-
-        def self.extract_contents_from_git_binary_delta_chunk(from_filepath, from_git_index, encoded_chunk, to_git_index)
-            to_filepath, to_filename = get_new_temp_filepath_and_name
-            from_filename = File.basename(from_filepath)
-            patch = FileDiff.git_changed_file_binary_patch(to_filename, from_filename, encoded_chunk, to_git_index, from_git_index)
-            run_git_apply_on_patch(to_filepath, patch)
-            return to_filepath
-        end
-
-        def self.extract_contents_of_from_revision(repository_path, encoded_chunk, git_index)
-            # For literal encoded, simply reconstruct.
-            if GIT_LITERAL_FORMAT.match(encoded_chunk[0])
-                return extract_contents_from_git_binary_literal_chunk(encoded_chunk, git_index)
-            end
-            #  For delta encoded, download from svn.
-            if GIT_DELTA_FORMAT.match(encoded_chunk[0])
-                return download_from_revision_from_svn(repository_path)
-            end
-            raise "Error: unknown git patch encoding"
-        end
-
-        def self.extract_contents_of_to_revision(repository_path, encoded_chunk, git_index, from_filepath, from_git_index)
-            # For literal encoded, simply reconstruct.
-            if GIT_LITERAL_FORMAT.match(encoded_chunk[0])
-                return extract_contents_from_git_binary_literal_chunk(encoded_chunk, git_index)
-            end
-            # For delta encoded, reconstruct using delta and previously constructed 'from' revision.
-            if GIT_DELTA_FORMAT.match(encoded_chunk[0])
-                return extract_contents_from_git_binary_delta_chunk(from_filepath, from_git_index, encoded_chunk, git_index)
-            end
-            raise "Error: unknown git patch encoding"
-        end
-    end
-
-    class DiffBlock
-        attr_accessor :parts
-
-        def initialize(container)
-            @parts = []
-            container << self
-        end
-
-        def to_html
-            str = "<div class='DiffBlock'>\n"
-            str += @parts.collect{ |part| part.to_html }.join
-            str += "<div class='clear_float'></div></div>\n"
-        end
-    end
-
-    class DiffBlockPart
-        attr_reader :className
-        attr :lines
-
-        def initialize(className, container)
-            $last_prettify_part_count[className] += 1
-            @className = className
-            @lines = []
-            container.parts << self
-        end
-
-        def to_html
-            str = "<div class='DiffBlockPart %s'>\n" % @className
-            str += @lines.collect{ |line| line.to_html }.join
-            # Don't put white-space after this so adjacent inline-block DiffBlockParts will not wrap.
-            str += "</div>"
-        end
-    end
-
-    class DiffSection
-        def initialize(lines)
-            lines.length >= 1 or raise "DiffSection.parse only received %d lines" % lines.length
-
-            matches = START_OF_SECTION_FORMAT.match(lines[0])
-
-            if matches
-                from, to = [matches[1].to_i, matches[3].to_i]
-                if matches[2] and matches[4]
-                    from_end = from + matches[2].to_i
-                    to_end = to + matches[4].to_i
-                end
-            end
-
-            @blocks = []
-            diff_block = nil
-            diff_block_part = nil
-
-            for line in lines[1...lines.length]
-                startOfLine = line =~ /^[-\+ ]/ ? 1 : 0
-                text = line[startOfLine...line.length].chomp
-                case line[0]
-                when ?-
-                    if (diff_block_part.nil? or diff_block_part.className != 'remove')
-                        diff_block = DiffBlock.new(@blocks)
-                        diff_block_part = DiffBlockPart.new('remove', diff_block)
-                    end
-
-                    diff_block_part.lines << CodeLine.new(from, nil, text)
-                    from += 1 unless from.nil?
-                when ?+
-                    if (diff_block_part.nil? or diff_block_part.className != 'add')
-                        # Put add lines that immediately follow remove lines into the same DiffBlock.
-                        if (diff_block.nil? or diff_block_part.className != 'remove')
-                            diff_block = DiffBlock.new(@blocks)
-                        end
-
-                        diff_block_part = DiffBlockPart.new('add', diff_block)
-                    end
-
-                    diff_block_part.lines << CodeLine.new(nil, to, text)
-                    to += 1 unless to.nil?
-                else
-                    if (diff_block_part.nil? or diff_block_part.className != 'shared')
-                        diff_block = DiffBlock.new(@blocks)
-                        diff_block_part = DiffBlockPart.new('shared', diff_block)
-                    end
-
-                    diff_block_part.lines << CodeLine.new(from, to, text)
-                    from += 1 unless from.nil?
-                    to += 1 unless to.nil?
-                end
-
-                break if from_end and to_end and from == from_end and to == to_end
-            end
-
-            changes = [ [ [], [] ] ]
-            for block in @blocks
-                for block_part in block.parts
-                    for line in block_part.lines
-                        if (!line.fromLineNumber.nil? and !line.toLineNumber.nil?) then
-                            changes << [ [], [] ]
-                            next
-                        end
-                        changes.last.first << line if line.toLineNumber.nil?
-                        changes.last.last << line if line.fromLineNumber.nil?
-                    end
-                end
-            end
-
-            for change in changes
-                next unless change.first.length == change.last.length
-                for i in (0...change.first.length)
-                    from_text = change.first[i].text
-                    to_text = change.last[i].text
-                    next if from_text.length > MAXIMUM_INTRALINE_DIFF_LINE_LENGTH or to_text.length > MAXIMUM_INTRALINE_DIFF_LINE_LENGTH
-                    raw_operations = HTMLDiff::DiffBuilder.new(from_text, to_text).operations
-                    operations = []
-                    back = 0
-                    raw_operations.each_with_index do |operation, j|
-                        if operation.action == :equal and j < raw_operations.length - 1
-                           length = operation.end_in_new - operation.start_in_new
-                           if length < SMALLEST_EQUAL_OPERATION
-                               back = length
-                               next
-                           end
-                        end
-                        operation.start_in_old -= back
-                        operation.start_in_new -= back
-                        back = 0
-                        operations << operation
-                    end
-                    change.first[i].operations = operations
-                    change.last[i].operations = operations
-                end
-            end
-
-            @blocks.unshift(ContextLine.new(matches[5])) unless matches.nil? || matches[5].empty?
-        end
-
-        def to_html
-            str = "<div class='DiffSection'>\n"
-            str += @blocks.collect{ |block| block.to_html }.join
-            str += "</div>\n"
-        end
-
-        def self.parse(lines)
-            linesForSections = lines.inject([[]]) do |sections, line|
-                sections << [] if line =~ /^@@/
-                sections.last << line
-                sections
-            end
-
-            linesForSections.delete_if { |lines| lines.nil? or lines.empty? }
-            linesForSections.collect { |lines| DiffSection.new(lines) }
-        end
-    end
-
-    class Line
-        attr_reader :fromLineNumber
-        attr_reader :toLineNumber
-        attr_reader :text
-
-        def initialize(from, to, text)
-            @fromLineNumber = from
-            @toLineNumber = to
-            @text = text
-        end
-
-        def text_as_html
-            CGI.escapeHTML(text)
-        end
-
-        def classes
-            lineClasses = ["Line", "LineContainer"]
-            lineClasses << ["add"] unless @toLineNumber.nil? or !@fromLineNumber.nil?
-            lineClasses << ["remove"] unless @fromLineNumber.nil? or !@toLineNumber.nil?
-            lineClasses
-        end
-
-        def to_html
-            markedUpText = self.text_as_html
-            str = "<div class='%s'>\n" % self.classes.join(' ')
-            str += "<span class='from lineNumber'>%s</span><span class='to lineNumber'>%s</span>" %
-                   [@fromLineNumber.nil? ? '&nbsp;' : @fromLineNumber,
-                    @toLineNumber.nil? ? '&nbsp;' : @toLineNumber] unless @fromLineNumber.nil? and @toLineNumber.nil?
-            str += "<span class='text'>%s</span>\n" % markedUpText
-            str += "</div>\n"
-        end
-    end
-
-    class CodeLine < Line
-        attr :operations, true
-
-        def text_as_html
-            html = []
-            tag = @fromLineNumber.nil? ? "ins" : "del"
-            if @operations.nil? or @operations.empty?
-                return CGI.escapeHTML(@text)
-            end
-            @operations.each do |operation|
-                start = @fromLineNumber.nil? ? operation.start_in_new : operation.start_in_old
-                eend = @fromLineNumber.nil? ? operation.end_in_new : operation.end_in_old
-                escaped_text = CGI.escapeHTML(@text[start...eend])
-                if eend - start === 0 or operation.action === :equal
-                    html << escaped_text
-                else
-                    html << "<#{tag}>#{escaped_text}</#{tag}>"
-                end
-            end
-            html.join
-        end
-    end
-
-    class ContextLine < Line
-        def initialize(context)
-            super("@", "@", context)
-        end
-
-        def classes
-            super << "context"
-        end
-    end
-end
diff --git a/third_party/blink/tools/blinkruby/PrettyPatch_test.rb b/third_party/blink/tools/blinkruby/PrettyPatch_test.rb
deleted file mode 100755
index 0d5f943..0000000
--- a/third_party/blink/tools/blinkruby/PrettyPatch_test.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/ruby
-
-require 'test/unit'
-require 'open-uri'
-require 'PrettyPatch'
-
-# Note: internet connection is needed to run this test suite.
-
-class PrettyPatch_test < Test::Unit::TestCase
-    class Info
-        TITLE = 0
-        FILE = 1
-        ADD = 2
-        REMOVE = 3
-        SHARED = 4
-    end
-
-    PATCHES = {
-        20510 => ["Single change", 1, 1, 0, 2],
-        20528 => ["No 'Index' or 'diff' in patch header", 1, 4, 3, 7],
-        21151 => ["Leading '/' in the path of files", 4, 9, 1, 16],
-        # Binary files use shared blocks, there are three in 30488.
-        30488 => ["Quoted filenames in git diff", 23, 28, 25, 64 + 3],
-        23920 => ["Mac line ending", 3, 3, 0, 5],
-        39615 => ["Git signature", 2, 2, 0, 3],
-        80852 => ["Changes one line plus ChangeLog", 2, 2, 1, 4],
-        83127 => ["Only add stuff", 2, 2, 0, 3],
-        85071 => ["Adds and removes from a file plus git signature", 2, 5, 3, 9],
-        106368 => ["Images with git delta binary patch", 69, 8, 23, 10],
-    }
-
-    def get_patch_uri(id)
-        "https://bugs.webkit.org/attachment.cgi?id=" + id.to_s
-    end
-
-    def get_patch(id)
-        result = nil
-        patch_uri = get_patch_uri(id)
-        begin
-            result = open(patch_uri) { |f| result = f.read }
-        rescue => exception
-            assert(false, "Fail to get patch " + patch_uri)
-        end
-        result
-    end
-
-    def check_one_patch(id, info)
-        patch = get_patch(id)
-        description = get_patch_uri(id)
-        description +=  " (" + info[Info::TITLE] + ")" unless info[Info::TITLE].nil?
-        puts "Testing " + description
-        pretty = nil
-        assert_nothing_raised("Crash while prettifying " + description) {
-            pretty = PrettyPatch.prettify(patch)
-        }
-        assert(pretty, "Empty result while prettifying " + description)
-        assert_equal(info[Info::FILE], $last_prettify_file_count, "Wrong number of files changed in " + description)
-        assert_equal(info[Info::ADD], $last_prettify_part_count["add"], "Wrong number of 'add' parts in " + description)
-        assert_equal(info[Info::REMOVE], $last_prettify_part_count["remove"], "Wrong number of 'remove' parts in " + description)
-        assert_equal(info[Info::SHARED], $last_prettify_part_count["shared"], "Wrong number of 'shared' parts in " + description)
-        assert_equal(0, $last_prettify_part_count["binary"], "Wrong number of 'binary' parts in " + description)
-        assert_equal(0, $last_prettify_part_count["extract-error"], "Wrong number of 'extract-error' parts in " + description)
-        return pretty
-    end
-
-    def test_patches
-        PATCHES.each { |id, info| check_one_patch(id, info) }
-    end
-
-    def test_images_without_checksum
-        pretty = check_one_patch(144064, ["Images without checksums", 10, 5, 4, 8])
-        matches = pretty.match("INVALID: Image lacks a checksum.")
-        # FIXME: This should match, but there's a bug when running the tests where the image data
-        # doesn't get properly written out to the temp files, so there is no image and we don't print
-        # the warning that the image is missing its checksum.
-        assert(!matches, "Should have invalid checksums")
-        # FIXME: This should only have 4 invalid images, but due to the above tempfile issue, there are 0.
-        assert_equal(0, pretty.scan(/INVALID\: Image lacks a checksum\./).size)
-    end
-
-    def test_new_image
-        pretty = check_one_patch(145881, ["New image", 19, 36, 19, 56])
-        matches = pretty.match("INVALID: Image lacks a checksum.")
-        assert(!matches, "Should not have invalid checksums")
-    end
-
-    def test_images_correctly_without_checksum_git
-        pretty = check_one_patch(101620, ["Images correctly without checksums git", 7, 15, 10, 26])
-        matches = pretty.match("INVALID: Image lacks a checksum.")
-        assert(!matches, "Png should lack a checksum without an error.")
-    end
-
-    def test_images_correctly_without_checksum_svn
-        pretty = check_one_patch(31202, ["Images correctly without checksums svn", 4, 4, 1, 4])
-        matches = pretty.match("INVALID: Image lacks a checksum.")
-        assert(!matches, "Png should lack a checksum without an error.")
-    end
-
-end
diff --git a/third_party/blink/tools/blinkruby/diff.rb b/third_party/blink/tools/blinkruby/diff.rb
deleted file mode 100644
index e5c154a6..0000000
--- a/third_party/blink/tools/blinkruby/diff.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-module HTMLDiff
-
-  Match = Struct.new(:start_in_old, :start_in_new, :size)
-  class Match
-    def end_in_old
-      self.start_in_old + self.size
-    end
-    
-    def end_in_new
-      self.start_in_new + self.size
-    end
-  end
-  
-  Operation = Struct.new(:action, :start_in_old, :end_in_old, :start_in_new, :end_in_new)
-
-  class DiffBuilder
-
-    def initialize(old_version, new_version)
-      @old_version, @new_version = old_version, new_version
-      split_inputs_to_words
-      index_new_words
-    end
-
-    def split_inputs_to_words
-      @old_words = explode(@old_version)
-      @new_words = explode(@new_version)
-    end
-
-    def index_new_words
-      @word_indices = Hash.new { |h, word| h[word] = [] }
-      @new_words.each_with_index { |word, i| @word_indices[word] << i }
-    end
-
-    def operations
-      position_in_old = position_in_new = 0
-      operations = []
-      
-      matches = matching_blocks
-      # an empty match at the end forces the loop below to handle the unmatched tails
-      # I'm sure it can be done more gracefully, but not at 23:52
-      matches << Match.new(@old_words.length, @new_words.length, 0)
-      
-      matches.each_with_index do |match, i|
-        match_starts_at_current_position_in_old = (position_in_old == match.start_in_old)
-        match_starts_at_current_position_in_new = (position_in_new == match.start_in_new)
-        
-        action_upto_match_positions = 
-          case [match_starts_at_current_position_in_old, match_starts_at_current_position_in_new]
-          when [false, false]
-            :replace
-          when [true, false]
-            :insert
-          when [false, true]
-            :delete
-          else
-            # this happens if the first few words are same in both versions
-            :none
-          end
-
-        if action_upto_match_positions != :none
-          operation_upto_match_positions = 
-              Operation.new(action_upto_match_positions, 
-                  position_in_old, match.start_in_old, 
-                  position_in_new, match.start_in_new)
-          operations << operation_upto_match_positions
-        end
-        if match.size != 0
-          match_operation = Operation.new(:equal, 
-              match.start_in_old, match.end_in_old, 
-              match.start_in_new, match.end_in_new)
-          operations << match_operation
-        end
-
-        position_in_old = match.end_in_old
-        position_in_new = match.end_in_new
-      end
-      
-      operations
-    end
-
-    def matching_blocks
-      matching_blocks = []
-      recursively_find_matching_blocks(0, @old_words.size, 0, @new_words.size, matching_blocks)
-      matching_blocks
-    end
-
-    def recursively_find_matching_blocks(start_in_old, end_in_old, start_in_new, end_in_new, matching_blocks)
-      match = find_match(start_in_old, end_in_old, start_in_new, end_in_new)
-      if match
-        if start_in_old < match.start_in_old and start_in_new < match.start_in_new
-          recursively_find_matching_blocks(
-              start_in_old, match.start_in_old, start_in_new, match.start_in_new, matching_blocks) 
-        end
-        matching_blocks << match
-        if match.end_in_old < end_in_old and match.end_in_new < end_in_new
-          recursively_find_matching_blocks(
-              match.end_in_old, end_in_old, match.end_in_new, end_in_new, matching_blocks)
-        end
-      end
-    end
-
-    def find_match(start_in_old, end_in_old, start_in_new, end_in_new)
-
-      best_match_in_old = start_in_old
-      best_match_in_new = start_in_new
-      best_match_size = 0
-      
-      match_length_at = Hash.new { |h, index| h[index] = 0 }
-      
-      start_in_old.upto(end_in_old - 1) do |index_in_old|
-
-        new_match_length_at = Hash.new { |h, index| h[index] = 0 }
-
-        @word_indices[@old_words[index_in_old]].each do |index_in_new|
-          next  if index_in_new < start_in_new
-          break if index_in_new >= end_in_new
-
-          new_match_length = match_length_at[index_in_new - 1] + 1
-          new_match_length_at[index_in_new] = new_match_length
-
-          if new_match_length > best_match_size
-            best_match_in_old = index_in_old - new_match_length + 1
-            best_match_in_new = index_in_new - new_match_length + 1
-            best_match_size = new_match_length
-          end
-        end
-        match_length_at = new_match_length_at
-      end
-
-#      best_match_in_old, best_match_in_new, best_match_size = add_matching_words_left(
-#          best_match_in_old, best_match_in_new, best_match_size, start_in_old, start_in_new)
-#      best_match_in_old, best_match_in_new, match_size = add_matching_words_right(
-#          best_match_in_old, best_match_in_new, best_match_size, end_in_old, end_in_new)
-
-      return (best_match_size != 0 ? Match.new(best_match_in_old, best_match_in_new, best_match_size) : nil)
-    end
-
-    def add_matching_words_left(match_in_old, match_in_new, match_size, start_in_old, start_in_new)
-      while match_in_old > start_in_old and 
-            match_in_new > start_in_new and 
-            @old_words[match_in_old - 1] == @new_words[match_in_new - 1]
-        match_in_old -= 1
-        match_in_new -= 1
-        match_size += 1
-      end
-      [match_in_old, match_in_new, match_size]
-    end
-
-    def add_matching_words_right(match_in_old, match_in_new, match_size, end_in_old, end_in_new)
-      while match_in_old + match_size < end_in_old and 
-            match_in_new + match_size < end_in_new and
-            @old_words[match_in_old + match_size] == @new_words[match_in_new + match_size]
-        match_size += 1
-      end
-      [match_in_old, match_in_new, match_size]
-    end
-
-    def explode(sequence)
-      sequence.is_a?(String) ? sequence.split(//) : sequence
-    end
-
-  end # of class Diff Builder
-  
-end
diff --git a/third_party/blink/tools/blinkruby/prettify.rb b/third_party/blink/tools/blinkruby/prettify.rb
deleted file mode 100755
index a9c211f..0000000
--- a/third_party/blink/tools/blinkruby/prettify.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'optparse'
-require 'pathname'
-require 'webrick/htmlutils'
-
-$LOAD_PATH << Pathname.new(__FILE__).dirname.realpath.to_s
-
-require 'PrettyPatch'
-
-BACKTRACE_SEPARATOR = "\n\tfrom "
-
-options = { :html_exceptions => false }
-OptionParser.new do |opts|
-    opts.banner = "Usage: #{File.basename($0)} [options] [patch-file]"
-
-    opts.separator ""
-
-    opts.on("--html-exceptions", "Print exceptions to stdout as HTML") { |h| options[:html_exceptions] = h }
-end.parse!
-
-patch_data = nil
-if ARGV.length == 0 || ARGV[0] == '-' then
-    patch_data = $stdin.read
-else
-    File.open(ARGV[0]) { |file| patch_data = file.read }
-end
-
-begin
-    puts PrettyPatch.prettify(patch_data)
-rescue => exception
-    raise unless options[:html_exceptions]
-
-    backtrace = exception.backtrace
-    backtrace[0] += ": " + exception + " (" + exception.class.to_s + ")"
-    print "<pre>\n", WEBrick::HTMLUtils::escape(backtrace.join(BACKTRACE_SEPARATOR)), "\n</pre>\n"
-end
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index b500ea18..12751a1a 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -728,6 +728,17 @@
 chrome.fileManagerPrivate.executeCustomAction = function(
     entries, actionId, callback) {};
 
+/**
+ * Returns true if crostini is enabled.
+ * @param {function(!boolean)} callback
+ */
+chrome.fileManagerPrivate.isCrostiniEnabled = function(callback) {};
+
+/**
+ * Starts and mounts crostini container.
+ */
+chrome.fileManagerPrivate.mountCrostiniContainer = function() {};
+
 /** @type {!ChromeEvent} */
 chrome.fileManagerPrivate.onMountCompleted;
 
diff --git a/third_party/fuchsia-sdk/fidl_library.gni b/third_party/fuchsia-sdk/fidl_library.gni
index dcb8dde..1dd93e05 100644
--- a/third_party/fuchsia-sdk/fidl_library.gni
+++ b/third_party/fuchsia-sdk/fidl_library.gni
@@ -4,6 +4,18 @@
 
 assert(is_fuchsia)
 
+# Template for FIDL libraries. Following parameters can be passed when
+# instantiating these templates:
+#   sources   - List of .fidl files.
+#   name      - (optional) Name of the library. Must match the name specified
+#               in the .fidl files. target_name is used if name is not specified
+#               explicitly.
+#   deps      - (optional) List of other fild_library() targets that this FIDL
+#               library depends on.
+#   namespace - (optional) Namespace for generated code. All classes in the
+#               generated code are placed under <namespace>::<name>:: namespace.
+#               'fuchsia' namespace is used by default.
+#
 template("fidl_library") {
   pkg_name = target_name
 
@@ -12,10 +24,16 @@
     pkg_name = invoker.name
   }
 
+  namespace = "fuchsia"
+  if (defined(invoker.namespace)) {
+    namespace = invoker.namespace
+  }
+
   response_file = "$target_gen_dir/$target_name.rsp"
   json_representation = "$target_gen_dir/$pkg_name.fidl.json"
   output_gen_base = "$target_gen_dir/fidl"
   output_gen_dir = "$output_gen_base/fuchsia/cpp"
+  output_gen_dir_no_ns = "$output_gen_base/no_ns/fuchsia/cpp"
   tables_file = "$output_gen_base/$pkg_name.fidl-tables.cc"
 
   action("${target_name}_response_file") {
@@ -31,7 +49,7 @@
                              "testonly",
                            ])
 
-    libraries_file = "$target_gen_dir/$pkg_name.fidl_libraries"
+    libraries_file = "$target_gen_dir/${invoker.target_name}.fidl_libraries"
 
     outputs = [
       response_file,
@@ -52,11 +70,21 @@
              "--sources",
            ] + rebase_path(sources, root_build_dir)
 
-    if (defined(invoker.fidl_deps)) {
+    if (defined(invoker.deps) || defined(invoker.public_deps)) {
+      merged_deps = []
+
+      if (defined(invoker.deps)) {
+        merged_deps += invoker.deps
+      }
+
+      if (defined(invoker.public_deps)) {
+        merged_deps += invoker.public_deps
+      }
+
       dep_libraries = []
       deps = []
 
-      foreach(dep, invoker.fidl_deps) {
+      foreach(dep, merged_deps) {
         gen_dir = get_label_info(dep, "target_gen_dir")
         name = get_label_info(dep, "name")
         dep_libraries += [ "$gen_dir/$name.fidl_libraries" ]
@@ -97,7 +125,7 @@
   }
 
   action("${target_name}_cpp_gen") {
-    visibility = [ ":${invoker.target_name}" ]
+    visibility = [ ":${invoker.target_name}_update_namespace" ]
 
     deps = [
       ":${invoker.target_name}_compile",
@@ -108,8 +136,8 @@
     ]
 
     outputs = [
-      "$output_gen_dir/$pkg_name.h",
-      "$output_gen_dir/$pkg_name.cc",
+      "${output_gen_dir_no_ns}/${pkg_name}.h",
+      "${output_gen_dir_no_ns}/${pkg_name}.cc",
     ]
 
     script = "//build/gn_run_binary.py"
@@ -121,9 +149,37 @@
       "-json",
       rebase_path("$json_representation"),
       "-include-base",
-      rebase_path("$output_gen_base"),
+      rebase_path("$output_gen_base/no_ns"),
       "-output-base",
-      rebase_path("$output_gen_dir/$pkg_name"),
+      rebase_path("${output_gen_dir_no_ns}/${pkg_name}"),
+    ]
+  }
+
+  # Move generated code to a different namespace to avoid conflicts with
+  # existing code in chromium.
+  # TODO(sergeyu): Remove this once FIDL-160 is resolved in Fuchsia.
+  action_foreach("${target_name}_update_namespace") {
+    visibility = [ ":${invoker.target_name}" ]
+
+    deps = [
+      ":${invoker.target_name}_cpp_gen",
+    ]
+
+    sources = [
+      "${output_gen_dir_no_ns}/${pkg_name}.cc",
+      "${output_gen_dir_no_ns}/${pkg_name}.h",
+    ]
+
+    outputs = [
+      "${output_gen_dir}/{{source_file_part}}",
+    ]
+
+    script = "//third_party/fuchsia-sdk/update_namespace.py"
+    args = [
+      namespace,
+      pkg_name,
+      "{{source}}",
+      rebase_path("${output_gen_dir}/{{source_file_part}}"),
     ]
   }
 
@@ -152,7 +208,7 @@
     }
     deps += [
       ":${invoker.target_name}_compile",
-      ":${invoker.target_name}_cpp_gen",
+      ":${invoker.target_name}_update_namespace",
     ]
 
     if (!defined(public_deps)) {
diff --git a/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni b/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
index ca7d3da..24d59a7 100644
--- a/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
+++ b/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
@@ -21,7 +21,6 @@
       name = pkg_name
       forward_variables_from(invoker,
                              [
-                               "deps",
                                "public_deps",
                                "testonly",
                                "visibility",
@@ -31,6 +30,13 @@
       foreach(file, invoker.fidl_files) {
         sources += [ "sdk/fidl/${pkg_name}/${file}" ]
       }
+
+      if (defined(invoker.fidl_deps)) {
+        deps = []
+        foreach(fidl_dep, invoker.fidl_deps) {
+          deps += [ "${fidl_dep}_fidl" ]
+        }
+      }
     }
   }
 
diff --git a/third_party/fuchsia-sdk/update_namespace.py b/third_party/fuchsia-sdk/update_namespace.py
new file mode 100755
index 0000000..8af1300
--- /dev/null
+++ b/third_party/fuchsia-sdk/update_namespace.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# 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.
+#
+# Helper script used to update namespace in C++ files generated by fidlgen. This
+# is necessary to avoid naming conflicts with existing code in chromium. For
+# example ui::Event FIDL interface collides with ui::Event class in chromium.
+# This script moves it to fuchsia::ui::Event.
+#
+# TODO(sergeyu): Remove this script once FIDL files in fuchsia are updated to
+# use fuchsia namespace.
+
+import sys
+import re
+
+def main(namespace, libname, input_file, output_file):
+  with open(input_file) as f:
+    text = f.read()
+
+  text = re.sub(r'^namespace\ %s\ \{$' % libname,
+                r'namespace %s {\nnamespace %s {' %
+                    (namespace, libname),
+                text, flags=re.MULTILINE)
+
+  text = re.sub(r'}\ \ // namespace\ %s$' % libname,
+                r'}  // namespace %s\n}  // namespace %s'
+                    % (libname, namespace),
+                text, flags=re.MULTILINE)
+
+  text = re.sub(r'(?<![a-z0-9_])%s::' % libname,
+                r'%s::%s::' % (namespace, libname),
+                text)
+
+  with open(output_file, "w") as f:
+    f.write(text)
+
+if __name__ == "__main__":
+  sys.exit(main(*sys.argv[1:]))
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 6e37546e..ca3c042 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -17,7 +17,7 @@
     "iron-icon": "PolymerElements/iron-icon#2.0.1",
     "iron-iconset-svg": "PolymerElements/iron-iconset-svg#2.1.1",
     "iron-icons": "PolymerElements/iron-icons#2.0.1",
-    "iron-input": "PolymerElements/iron-input#1.0.10",
+    "iron-input": "PolymerElements/iron-input#2.1.3",
     "iron-list": "PolymerElements/iron-list#2.0.14",
     "iron-location": "PolymerElements/iron-location#2.0.3",
     "iron-media-query": "PolymerElements/iron-media-query#2.0.0",
diff --git a/third_party/polymer/v1_0/chromium.patch b/third_party/polymer/v1_0/chromium.patch
index 3873231..a5ea235 100644
--- a/third_party/polymer/v1_0/chromium.patch
+++ b/third_party/polymer/v1_0/chromium.patch
@@ -9,6 +9,35 @@
 -<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic">
 -<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,700">
 +<link rel="stylesheet" href="chrome://resources/css/roboto.css">
+diff --git a/components-chromium/paper-input/paper-input-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js
+index 754820fb5d0c..3f8e640ffa71 100644
+--- a/components-chromium/paper-input/paper-input-extracted.js
++++ b/components-chromium/paper-input/paper-input-extracted.js
+@@ -7,13 +7,7 @@ Polymer({
+     ],
+ 
+     beforeRegister: function() {
+-      // We need to tell which kind of of template to stamp based on
+-      // what kind of `iron-input` we got, but because of polyfills and
+-      // custom elements differences between v0 and v1, the safest bet is
+-      // to check a particular method we know the iron-input#2.x can have.
+-      // If it doesn't have it, then it's an iron-input#1.x.
+-      var ironInput = document.createElement('iron-input');
+-      var version = typeof ironInput._initSlottedInput == 'function' ? 'v1' : 'v0';
++      var version = 'v1';  // Hard coded Polymer 2 style iron-input.
+       var template = Polymer.DomModule.import('paper-input', 'template');
+       var inputTemplate = Polymer.DomModule.import('paper-input', 'template#' + version);
+       var inputPlaceholder = template.content.querySelector('#template-placeholder');
+@@ -30,7 +24,8 @@ Polymer({
+      * @return {!HTMLElement}
+      */
+     get _focusableElement() {
+-      return Polymer.Element ? this.inputElement._inputElement : this.inputElement;
++      // TODO(hcarmona): remove this patch after polymer is v2.
++      return this.inputElement._inputElement;
+     },
+ 
+     // Note: This event is only available in the 1.0 version of this element.
 diff --git a/components-chromium/paper-input/paper-textarea-extracted.js b/components-chromium/paper-input/paper-textarea-extracted.js
 index 11bc05a37471..8d425e02c30b 100644
 --- a/components-chromium/paper-input/paper-textarea-extracted.js
diff --git a/third_party/polymer/v1_0/components-chromium/iron-input/bower.json b/third_party/polymer/v1_0/components-chromium/iron-input/bower.json
index 018f4a16..5797690 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-input/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/iron-input/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "iron-input",
-  "version": "1.0.10",
+  "version": "2.1.3",
   "description": "An input element with data binding",
   "authors": [
     "The Polymer Authors"
@@ -20,16 +20,41 @@
   "homepage": "https://github.com/PolymerElements/iron-input",
   "ignore": [],
   "dependencies": {
-    "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
-    "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
-    "polymer": "Polymer/polymer#^1.0.0"
+    "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#1 - 2",
+    "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#1 - 2",
+    "polymer": "Polymer/polymer#1.9 - 2"
   },
   "devDependencies": {
-    "paper-styles": "polymerelements/paper-styles#^1.0.2",
-    "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
-    "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
-    "test-fixture": "PolymerElements/test-fixture#^1.0.0",
-    "web-component-tester": "^4.0.0",
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+    "iron-component-page": "polymerelements/iron-component-page#1 - 2",
+    "iron-demo-helpers": "polymerelements/iron-demo-helpers#1 - 2",
+    "iron-validator-behavior": "PolymerElements/iron-validator-behavior#1 - 2",
+    "paper-styles": "polymerelements/paper-styles#1 - 2",
+    "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1",
+    "web-component-tester": "^6.0.0",
+    "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0"
+  },
+  "variants": {
+    "1.x": {
+      "dependencies": {
+        "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
+        "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+        "polymer": "Polymer/polymer#^1.9"
+      },
+      "devDependencies": {
+        "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+        "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
+        "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+        "paper-styles": "polymerelements/paper-styles#^1.0.0",
+        "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+        "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+        "web-component-tester": "Polymer/web-component-tester#^4.0.0"
+      },
+      "resolutions": {
+        "webcomponentsjs": "^0.7"
+      }
+    }
+  },
+  "resolutions": {
+    "webcomponentsjs": "^1.0.0"
   }
 }
diff --git a/third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js
index 340dfc6..d0522f20 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-input/iron-input-extracted.js
@@ -1,288 +1,262 @@
-/*
-`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior`
-to `<input>`.
+Polymer({
+      is: 'iron-input',
 
-### Two-way binding
-
-By default you can only get notified of changes to an `input`'s `value` due to user input:
-
-    <input value="{{myValue::input}}">
-
-`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used
-for two-way data binding. `bind-value` will notify if it is changed either by user input or by script.
-
-    <input is="iron-input" bind-value="{{myValue}}">
-
-### Custom validators
-
-You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`.
-
-    <input is="iron-input" validator="my-custom-validator">
-
-### Stopping invalid input
-
-It may be desirable to only allow users to enter certain characters. You can use the
-`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature
-is separate from validation, and `allowed-pattern` does not affect how the input is validated.
-
-    <!-- only allow characters that match [0-9] -->
-    <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
-
-@hero hero.svg
-@demo demo/index.html
-*/
-
-  Polymer({
-
-    is: 'iron-input',
-
-    extends: 'input',
-
-    behaviors: [
-      Polymer.IronValidatableBehavior
-    ],
-
-    properties: {
+      behaviors: [Polymer.IronValidatableBehavior],
 
       /**
-       * Use this property instead of `value` for two-way data binding.
+       * Fired whenever `validate()` is called.
+       *
+       * @event iron-input-validate
        */
-      bindValue: {
-        observer: '_bindValueChanged',
-        type: String
+
+      properties: {
+
+        /**
+         * Use this property instead of `value` for two-way data binding, or to
+         * set a default value for the input. **Do not** use the distributed
+         * input's `value` property to set a default value.
+         */
+        bindValue: {type: String, value: ''},
+
+        /**
+         * Computed property that echoes `bindValue` (mostly used for Polymer 1.0
+         * backcompatibility, if you were one-way binding to the Polymer 1.0
+         * `input is="iron-input"` value attribute).
+         */
+        value: {type: String, computed: '_computeValue(bindValue)'},
+
+        /**
+         * Regex-like list of characters allowed as input; all characters not in the
+         * list will be rejected. The recommended format should be a list of allowed
+         * characters, for example, `[a-zA-Z0-9.+-!;:]`.
+         *
+         * This pattern represents the allowed characters for the field; as the user
+         * inputs text, each individual character will be checked against the
+         * pattern (rather than checking the entire value as a whole). If a
+         * character is not a match, it will be rejected.
+         *
+         * Pasted input will have each character checked individually; if any
+         * character doesn't match `allowedPattern`, the entire pasted string will
+         * be rejected.
+         *
+         * Note: if you were using `iron-input` in 1.0, you were also required to
+         * set `prevent-invalid-input`. This is no longer needed as of Polymer 2.0,
+         * and will be set automatically for you if an `allowedPattern` is provided.
+         *
+         */
+        allowedPattern: {type: String},
+
+        /**
+         * Set to true to auto-validate the input value as you type.
+         */
+        autoValidate: {type: Boolean, value: false},
+
+        /**
+         * The native input element.
+         */
+        _inputElement: Object,
       },
 
-      /**
-       * Set to true to prevent the user from entering invalid input. If `allowedPattern` is set,
-       * any character typed by the user will be matched against that pattern, and rejected if it's not a match.
-       * Pasted input will have each character checked individually; if any character
-       * doesn't match `allowedPattern`, the entire pasted string will be rejected.
-       * If `allowedPattern` is not set, it will use the `type` attribute (only supported for `type=number`).
-       */
-      preventInvalidInput: {
-        type: Boolean
+      observers: ['_bindValueChanged(bindValue, _inputElement)'],
+
+      listeners: {'input': '_onInput', 'keypress': '_onKeypress'},
+
+      created: function() {
+        Polymer.IronA11yAnnouncer.requestAvailability();
+        this._previousValidInput = '';
+        this._patternAlreadyChecked = false;
       },
 
-      /**
-       * Regular expression that list the characters allowed as input.
-       * This pattern represents the allowed characters for the field; as the user inputs text,
-       * each individual character will be checked against the pattern (rather than checking
-       * the entire value as a whole). The recommended format should be a list of allowed characters;
-       * for example, `[a-zA-Z0-9.+-!;:]`
-       */
-      allowedPattern: {
-        type: String,
-        observer: "_allowedPatternChanged"
+      attached: function() {
+        // If the input is added at a later time, update the internal reference.
+        this._observer = Polymer.dom(this).observeNodes(function(info) {
+          this._initSlottedInput();
+        }.bind(this));
       },
 
-      _previousValidInput: {
-        type: String,
-        value: ''
-      },
-
-      _patternAlreadyChecked: {
-        type: Boolean,
-        value: false
-      }
-
-    },
-
-    listeners: {
-      'input': '_onInput',
-      'keypress': '_onKeypress'
-    },
-
-    /** @suppress {checkTypes} */
-    registered: function() {
-      // Feature detect whether we need to patch dispatchEvent (i.e. on FF and IE).
-      if (!this._canDispatchEventOnDisabled()) {
-        this._origDispatchEvent = this.dispatchEvent;
-        this.dispatchEvent = this._dispatchEventFirefoxIE;
-      }
-    },
-
-    created: function() {
-      Polymer.IronA11yAnnouncer.requestAvailability();
-    },
-
-    _canDispatchEventOnDisabled: function() {
-      var input = document.createElement('input');
-      var canDispatch = false;
-      input.disabled = true;
-
-      input.addEventListener('feature-check-dispatch-event', function() {
-        canDispatch = true;
-      });
-
-      try {
-        input.dispatchEvent(new Event('feature-check-dispatch-event'));
-      } catch(e) {}
-
-      return canDispatch;
-    },
-
-    _dispatchEventFirefoxIE: function() {
-      // Due to Firefox bug, events fired on disabled form controls can throw
-      // errors; furthermore, neither IE nor Firefox will actually dispatch
-      // events from disabled form controls; as such, we toggle disable around
-      // the dispatch to allow notifying properties to notify
-      // See issue #47 for details
-      var disabled = this.disabled;
-      this.disabled = false;
-      this._origDispatchEvent.apply(this, arguments);
-      this.disabled = disabled;
-    },
-
-    get _patternRegExp() {
-      var pattern;
-      if (this.allowedPattern) {
-        pattern = new RegExp(this.allowedPattern);
-      } else {
-        switch (this.type) {
-          case 'number':
-            pattern = /[0-9.,e-]/;
-            break;
+      detached: function() {
+        if (this._observer) {
+          Polymer.dom(this).unobserveNodes(this._observer);
+          this._observer = null;
         }
-      }
-      return pattern;
-    },
+      },
 
-    ready: function() {
-      this.bindValue = this.value;
-    },
+      /**
+       * Returns the distributed input element.
+       */
+      get inputElement() {
+        return this._inputElement;
+      },
 
-    /**
-     * @suppress {checkTypes}
-     */
-    _bindValueChanged: function() {
-      if (this.value !== this.bindValue) {
-        this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue;
-      }
-      // manually notify because we don't want to notify until after setting value
-      this.fire('bind-value-changed', {value: this.bindValue});
-    },
+      _initSlottedInput: function() {
+        this._inputElement = this.getEffectiveChildren()[0];
 
-    _allowedPatternChanged: function() {
-      // Force to prevent invalid input when an `allowed-pattern` is set
-      this.preventInvalidInput = this.allowedPattern ? true : false;
-    },
-
-    _onInput: function() {
-      // Need to validate each of the characters pasted if they haven't
-      // been validated inside `_onKeypress` already.
-      if (this.preventInvalidInput && !this._patternAlreadyChecked) {
-        var valid = this._checkPatternValidity();
-        if (!valid) {
-          this._announceInvalidCharacter('Invalid string of characters not entered.');
-          this.value = this._previousValidInput;
+        if (this.inputElement && this.inputElement.value) {
+          this.bindValue = this.inputElement.value;
         }
-      }
 
-      this.bindValue = this.value;
-      this._previousValidInput = this.value;
-      this._patternAlreadyChecked = false;
-    },
+        this.fire('iron-input-ready');
+      },
 
-    _isPrintable: function(event) {
-      // What a control/printable character is varies wildly based on the browser.
-      // - most control characters (arrows, backspace) do not send a `keypress` event
-      //   in Chrome, but the *do* on Firefox
-      // - in Firefox, when they do send a `keypress` event, control chars have
-      //   a charCode = 0, keyCode = xx (for ex. 40 for down arrow)
-      // - printable characters always send a keypress event.
-      // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode
-      //   always matches the charCode.
-      // None of this makes any sense.
+      get _patternRegExp() {
+        var pattern;
+        if (this.allowedPattern) {
+          pattern = new RegExp(this.allowedPattern);
+        } else {
+          switch (this.inputElement.type) {
+            case 'number':
+              pattern = /[0-9.,e-]/;
+              break;
+          }
+        }
+        return pattern;
+      },
 
-      // For these keys, ASCII code == browser keycode.
-      var anyNonPrintable =
-        (event.keyCode == 8)   ||  // backspace
-        (event.keyCode == 9)   ||  // tab
-        (event.keyCode == 13)  ||  // enter
-        (event.keyCode == 27);     // escape
+      /**
+       * @suppress {checkTypes}
+       */
+      _bindValueChanged: function(bindValue, inputElement) {
+        // The observer could have run before attached() when we have actually
+        // initialized this property.
+        if (!inputElement) {
+          return;
+        }
 
-      // For these keys, make sure it's a browser keycode and not an ASCII code.
-      var mozNonPrintable =
-        (event.keyCode == 19)  ||  // pause
-        (event.keyCode == 20)  ||  // caps lock
-        (event.keyCode == 45)  ||  // insert
-        (event.keyCode == 46)  ||  // delete
-        (event.keyCode == 144) ||  // num lock
-        (event.keyCode == 145) ||  // scroll lock
-        (event.keyCode > 32 && event.keyCode < 41)   || // page up/down, end, home, arrows
-        (event.keyCode > 111 && event.keyCode < 124); // fn keys
+        if (bindValue === undefined) {
+          inputElement.value = null;
+        } else if (bindValue !== inputElement.value) {
+          this.inputElement.value = bindValue;
+        }
 
-      return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
-    },
+        if (this.autoValidate) {
+          this.validate();
+        }
 
-    _onKeypress: function(event) {
-      if (!this.preventInvalidInput && this.type !== 'number') {
-        return;
-      }
-      var regexp = this._patternRegExp;
-      if (!regexp) {
-        return;
-      }
+        // manually notify because we don't want to notify until after setting value
+        this.fire('bind-value-changed', {value: bindValue});
+      },
 
-      // Handle special keys and backspace
-      if (event.metaKey || event.ctrlKey || event.altKey)
-        return;
+      _onInput: function() {
+        // Need to validate each of the characters pasted if they haven't
+        // been validated inside `_onKeypress` already.
+        if (this.allowedPattern && !this._patternAlreadyChecked) {
+          var valid = this._checkPatternValidity();
+          if (!valid) {
+            this._announceInvalidCharacter(
+                'Invalid string of characters not entered.');
+            this.inputElement.value = this._previousValidInput;
+          }
+        }
+        this.bindValue = this._previousValidInput = this.inputElement.value;
+        this._patternAlreadyChecked = false;
+      },
 
-      // Check the pattern either here or in `_onInput`, but not in both.
-      this._patternAlreadyChecked = true;
+      _isPrintable: function(event) {
+        // What a control/printable character is varies wildly based on the browser.
+        // - most control characters (arrows, backspace) do not send a `keypress`
+        // event
+        //   in Chrome, but the *do* on Firefox
+        // - in Firefox, when they do send a `keypress` event, control chars have
+        //   a charCode = 0, keyCode = xx (for ex. 40 for down arrow)
+        // - printable characters always send a keypress event.
+        // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the
+        // keyCode
+        //   always matches the charCode.
+        // None of this makes any sense.
 
-      var thisChar = String.fromCharCode(event.charCode);
-      if (this._isPrintable(event) && !regexp.test(thisChar)) {
-        event.preventDefault();
-        this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.');
-      }
-    },
+        // For these keys, ASCII code == browser keycode.
+        var anyNonPrintable = (event.keyCode == 8) ||  // backspace
+            (event.keyCode == 9) ||                    // tab
+            (event.keyCode == 13) ||                   // enter
+            (event.keyCode == 27);                     // escape
 
-    _checkPatternValidity: function() {
-      var regexp = this._patternRegExp;
-      if (!regexp) {
+        // For these keys, make sure it's a browser keycode and not an ASCII code.
+        var mozNonPrintable = (event.keyCode == 19) ||  // pause
+            (event.keyCode == 20) ||                    // caps lock
+            (event.keyCode == 45) ||                    // insert
+            (event.keyCode == 46) ||                    // delete
+            (event.keyCode == 144) ||                   // num lock
+            (event.keyCode == 145) ||                   // scroll lock
+            (event.keyCode > 32 &&
+             event.keyCode < 41) ||  // page up/down, end, home, arrows
+            (event.keyCode > 111 && event.keyCode < 124);  // fn keys
+
+        return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
+      },
+
+      _onKeypress: function(event) {
+        if (!this.allowedPattern && this.inputElement.type !== 'number') {
+          return;
+        }
+        var regexp = this._patternRegExp;
+        if (!regexp) {
+          return;
+        }
+
+        // Handle special keys and backspace
+        if (event.metaKey || event.ctrlKey || event.altKey) {
+          return;
+        }
+
+        // Check the pattern either here or in `_onInput`, but not in both.
+        this._patternAlreadyChecked = true;
+
+        var thisChar = String.fromCharCode(event.charCode);
+        if (this._isPrintable(event) && !regexp.test(thisChar)) {
+          event.preventDefault();
+          this._announceInvalidCharacter(
+              'Invalid character ' + thisChar + ' not entered.');
+        }
+      },
+
+      _checkPatternValidity: function() {
+        var regexp = this._patternRegExp;
+        if (!regexp) {
+          return true;
+        }
+        for (var i = 0; i < this.inputElement.value.length; i++) {
+          if (!regexp.test(this.inputElement.value[i])) {
+            return false;
+          }
+        }
         return true;
-      }
-      for (var i = 0; i < this.value.length; i++) {
-        if (!regexp.test(this.value[i])) {
-          return false;
+      },
+
+      /**
+       * Returns true if `value` is valid. The validator provided in `validator`
+       * will be used first, then any constraints.
+       * @return {boolean} True if the value is valid.
+       */
+      validate: function() {
+        if (!this.inputElement) {
+          this.invalid = false;
+          return true;
         }
-      }
-      return true;
-    },
 
-    /**
-     * Returns true if `value` is valid. The validator provided in `validator` will be used first,
-     * then any constraints.
-     * @return {boolean} True if the value is valid.
-     */
-    validate: function() {
-      // First, check what the browser thinks. Some inputs (like type=number)
-      // behave weirdly and will set the value to "" if something invalid is
-      // entered, but will set the validity correctly.
-      var valid =  this.checkValidity();
+        // Use the nested input's native validity.
+        var valid = this.inputElement.checkValidity();
 
-      // Only do extra checking if the browser thought this was valid.
-      if (valid) {
-        // Empty, required input is invalid
-        if (this.required && this.value === '') {
-          valid = false;
-        } else if (this.hasValidator()) {
-          valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
+        // Only do extra checking if the browser thought this was valid.
+        if (valid) {
+          // Empty, required input is invalid
+          if (this.required && this.bindValue === '') {
+            valid = false;
+          } else if (this.hasValidator()) {
+            valid =
+                Polymer.IronValidatableBehavior.validate.call(this, this.bindValue);
+          }
         }
+
+        this.invalid = !valid;
+        this.fire('iron-input-validate');
+        return valid;
+      },
+
+      _announceInvalidCharacter: function(message) {
+        this.fire('iron-announce', {text: message});
+      },
+
+      _computeValue: function(bindValue) {
+        return bindValue;
       }
-
-      this.invalid = !valid;
-      this.fire('iron-input-validate');
-      return valid;
-    },
-
-    _announceInvalidCharacter: function(message) {
-      this.fire('iron-announce', { text: message });
-    }
-  });
-
-  /*
-  The `iron-input-validate` event is fired whenever `validate()` is called.
-  @event iron-input-validate
-  */
\ No newline at end of file
+    });
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/iron-input/iron-input.html b/third_party/polymer/v1_0/components-chromium/iron-input/iron-input.html
index 1225baa5..844735b 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-input/iron-input.html
+++ b/third_party/polymer/v1_0/components-chromium/iron-input/iron-input.html
@@ -10,4 +10,92 @@
 <link rel="import" href="../iron-a11y-announcer/iron-a11y-announcer.html">
 <link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
 
-</head><body><script src="iron-input-extracted.js"></script></body></html>
\ No newline at end of file
+<!--
+`<iron-input>` is a wrapper to a native `<input>` element, that adds two-way binding
+and prevention of invalid input. To use it, you must distribute a native `<input>`
+yourself. You can continue to use the native `input` as you would normally:
+
+    <iron-input>
+      <input>
+    </iron-input>
+
+    <iron-input>
+      <input type="email" disabled>
+    </iron-input>
+
+### Two-way binding
+
+By default you can only get notified of changes to a native `<input>`'s `value`
+due to user input:
+
+    <input value="{{myValue::input}}">
+
+This means that if you imperatively set the value (i.e. `someNativeInput.value = 'foo'`),
+no events will be fired and this change cannot be observed.
+
+`iron-input` adds the `bind-value` property that mirrors the native `input`'s '`value` property; this
+property can be used for two-way data binding.
+`bind-value` will notify if it is changed either by user input or by script.
+
+    <iron-input bind-value="{{myValue}}">
+      <input>
+    </iron-input>
+
+Note: this means that if you want to imperatively set the native `input`'s, you _must_
+set `bind-value` instead, so that the wrapper `iron-input` can be notified.
+
+### Validation
+
+`iron-input` uses the native `input`'s validation. For simplicity, `iron-input`
+has a `validate()` method (which internally just checks the distributed `input`'s
+validity), which sets an `invalid` attribute that can also be used for styling.
+
+To validate automatically as you type, you can use the `auto-validate` attribute.
+
+`iron-input` also fires an `iron-input-validate` event after `validate()` is
+called. You can use it to implement a custom validator:
+
+    var CatsOnlyValidator = {
+      validate: function(ironInput) {
+        var valid = !ironInput.bindValue || ironInput.bindValue === 'cat';
+        ironInput.invalid = !valid;
+        return valid;
+      }
+    }
+    ironInput.addEventListener('iron-input-validate', function() {
+      CatsOnly.validate(input2);
+    });
+
+You can also use an element implementing an [`IronValidatorBehavior`](/element/PolymerElements/iron-validatable-behavior).
+This example can also be found in the demo for this element:
+
+    <iron-input validator="cats-only">
+      <input>
+    </iron-input>
+
+### Preventing invalid input
+
+It may be desirable to only allow users to enter certain characters. You can use the
+`allowed-pattern` attribute to accomplish this. This feature
+is separate from validation, and `allowed-pattern` does not affect how the input is validated.
+
+    // Only allow typing digits, but a valid input has exactly 5 digits.
+    <iron-input allowed-pattern="[0-9]">
+      <input pattern="\d{5}">
+    </iron-input>
+
+@hero hero.svg
+@demo demo/index.html
+-->
+
+</head><body><dom-module id="iron-input">
+  <template>
+    <style>
+      :host {
+        display: inline-block;
+      }
+    </style>
+    <slot id="content"></slot>
+  </template>
+  </dom-module>
+<script src="iron-input-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js
index bd4bd0980..3cb605bba 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/paper-input/paper-input-extracted.js
@@ -7,13 +7,7 @@
     ],
 
     beforeRegister: function() {
-      // We need to tell which kind of of template to stamp based on
-      // what kind of `iron-input` we got, but because of polyfills and
-      // custom elements differences between v0 and v1, the safest bet is
-      // to check a particular method we know the iron-input#2.x can have.
-      // If it doesn't have it, then it's an iron-input#1.x.
-      var ironInput = document.createElement('iron-input');
-      var version = typeof ironInput._initSlottedInput == 'function' ? 'v1' : 'v0';
+      var version = 'v1';  // Hard coded Polymer 2 style iron-input.
       var template = Polymer.DomModule.import('paper-input', 'template');
       var inputTemplate = Polymer.DomModule.import('paper-input', 'template#' + version);
       var inputPlaceholder = template.content.querySelector('#template-placeholder');
@@ -30,7 +24,8 @@
      * @return {!HTMLElement}
      */
     get _focusableElement() {
-      return Polymer.Element ? this.inputElement._inputElement : this.inputElement;
+      // TODO(hcarmona): remove this patch after polymer is v2.
+      return this.inputElement._inputElement;
     },
 
     // Note: This event is only available in the 1.0 version of this element.
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index b7fc464..a8aee486 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -90,9 +90,9 @@
 
 Name: iron-input
 Repository: https://github.com/PolymerElements/iron-input.git
-Tree: 1.0.10
-Revision: 01d17407672ad8033ee447c9c7a65162f13c8f49
-Tree link: https://github.com/PolymerElements/iron-input/tree/1.0.10
+Tree: v2.1.3
+Revision: 72a4a9e898a4b825602eacd78944e8e25ee41f5c
+Tree link: https://github.com/PolymerElements/iron-input/tree/v2.1.3
 
 Name: iron-list
 Repository: https://github.com/PolymerElements/iron-list.git
diff --git a/third_party/protobuf/.gitignore b/third_party/protobuf/.gitignore
index 51d092c..a130b8b 100644
--- a/third_party/protobuf/.gitignore
+++ b/third_party/protobuf/.gitignore
@@ -53,6 +53,7 @@
 *.pyc
 *.egg-info
 *_pb2.py
+!python/google/protobuf/descriptor_pb2.py
 python/*.egg
 python/.eggs/
 python/.tox
diff --git a/third_party/protobuf/patches/0021-Fix-protobuf-s-library-.gitinore-file.patch b/third_party/protobuf/patches/0021-Fix-protobuf-s-library-.gitinore-file.patch
new file mode 100644
index 0000000..014abf9
--- /dev/null
+++ b/third_party/protobuf/patches/0021-Fix-protobuf-s-library-.gitinore-file.patch
@@ -0,0 +1,25 @@
+From db78edb2e3f0a60317b167b3a4002d0f5aa5a2a7 Mon Sep 17 00:00:00 2001
+From: Artem Titov <titovartem@chromium.org>
+Date: Mon, 7 May 2018 13:13:08 +0200
+Subject: [PATCH] Fix protobuf's library .gitinore file. Fix .gitignore file
+ for protobuf library not to exclude file, that is checkid in into repo.
+
+---
+ third_party/protobuf/.gitignore | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/third_party/protobuf/.gitignore b/third_party/protobuf/.gitignore
+index 51d092c72475..a130b8bcc028 100644
+--- a/third_party/protobuf/.gitignore
++++ b/third_party/protobuf/.gitignore
+@@ -53,6 +53,7 @@ src/google/protobuf/util/**/*.pb.h
+ *.pyc
+ *.egg-info
+ *_pb2.py
++!python/google/protobuf/descriptor_pb2.py
+ python/*.egg
+ python/.eggs/
+ python/.tox
+-- 
+2.17.0.441.gb46fe60e1d-goog
+
diff --git a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
index 5c788192..f9d9b484 100644
--- a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
+++ b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.14.0 */
+/* Generated by wayland-scanner 1.13.0 */
 
 #ifndef REMOTE_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
 #define REMOTE_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
@@ -1778,12 +1778,17 @@
 }
 
 #define ZCR_NOTIFICATION_SURFACE_V1_DESTROY 0
+#define ZCR_NOTIFICATION_SURFACE_V1_SET_APP_ID 1
 
 
 /**
  * @ingroup iface_zcr_notification_surface_v1
  */
 #define ZCR_NOTIFICATION_SURFACE_V1_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_zcr_notification_surface_v1
+ */
+#define ZCR_NOTIFICATION_SURFACE_V1_SET_APP_ID_SINCE_VERSION 2
 
 /** @ingroup iface_zcr_notification_surface_v1 */
 static inline void
@@ -1819,6 +1824,18 @@
 	wl_proxy_destroy((struct wl_proxy *) zcr_notification_surface_v1);
 }
 
+/**
+ * @ingroup iface_zcr_notification_surface_v1
+ *
+ * Set an application identifier for the notification surface.
+ */
+static inline void
+zcr_notification_surface_v1_set_app_id(struct zcr_notification_surface_v1 *zcr_notification_surface_v1, const char *app_id)
+{
+	wl_proxy_marshal((struct wl_proxy *) zcr_notification_surface_v1,
+			 ZCR_NOTIFICATION_SURFACE_V1_SET_APP_ID, app_id);
+}
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
index 4c9a3b7..24568d8 100644
--- a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
+++ b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.14.0 */
+/* Generated by wayland-scanner 1.13.0 */
 
 #ifndef REMOTE_SHELL_UNSTABLE_V1_SERVER_PROTOCOL_H
 #define REMOTE_SHELL_UNSTABLE_V1_SERVER_PROTOCOL_H
@@ -1494,6 +1494,15 @@
 	 */
 	void (*destroy)(struct wl_client *client,
 			struct wl_resource *resource);
+	/**
+	 * set application ID
+	 *
+	 * Set an application identifier for the notification surface.
+	 * @since 2
+	 */
+	void (*set_app_id)(struct wl_client *client,
+			   struct wl_resource *resource,
+			   const char *app_id);
 };
 
 
@@ -1501,6 +1510,10 @@
  * @ingroup iface_zcr_notification_surface_v1
  */
 #define ZCR_NOTIFICATION_SURFACE_V1_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_zcr_notification_surface_v1
+ */
+#define ZCR_NOTIFICATION_SURFACE_V1_SET_APP_ID_SINCE_VERSION 2
 
 #ifdef  __cplusplus
 }
diff --git a/third_party/wayland-protocols/protocol/remote-shell-protocol.c b/third_party/wayland-protocols/protocol/remote-shell-protocol.c
index b23c9fa..22d79d5 100644
--- a/third_party/wayland-protocols/protocol/remote-shell-protocol.c
+++ b/third_party/wayland-protocols/protocol/remote-shell-protocol.c
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.14.0 */
+/* Generated by wayland-scanner 1.13.0 */
 
 /*
  * Copyright 2016 The Chromium Authors.
@@ -137,11 +137,12 @@
 
 static const struct wl_message zcr_notification_surface_v1_requests[] = {
 	{ "destroy", "", types + 0 },
+	{ "set_app_id", "2s", types + 0 },
 };
 
 WL_EXPORT const struct wl_interface zcr_notification_surface_v1_interface = {
-	"zcr_notification_surface_v1", 1,
-	1, zcr_notification_surface_v1_requests,
+	"zcr_notification_surface_v1", 2,
+	2, zcr_notification_surface_v1_requests,
 	0, NULL,
 };
 
diff --git a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
index b31392ac..30e0e7b 100644
--- a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
+++ b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
@@ -894,7 +894,7 @@
 
   </interface>
 
-  <interface name="zcr_notification_surface_v1" version="1">
+  <interface name="zcr_notification_surface_v1" version="2">
     <description summary="A notification window">
       An interface that may be implemented by a wl_surface to host
       notification contents.
@@ -905,6 +905,15 @@
 	Unmap and destroy the notification surface.
       </description>
     </request>
+
+    <!-- Version 2 additions -->
+
+    <request name="set_app_id" since="2">
+      <description summary="set application ID">
+        Set an application identifier for the notification surface.
+      </description>
+      <arg name="app_id" type="string"/>
+    </request>
   </interface>
 
 </protocol>
diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py
index 0a3903c..52597dd 100755
--- a/tools/clang/scripts/run_tool.py
+++ b/tools/clang/scripts/run_tool.py
@@ -48,7 +48,6 @@
 """
 
 import argparse
-from collections import namedtuple
 import functools
 import json
 import multiprocessing
@@ -56,7 +55,6 @@
 import os.path
 import re
 import subprocess
-import shlex
 import sys
 
 script_dir = os.path.dirname(os.path.realpath(__file__))
@@ -66,8 +64,6 @@
 from clang import compile_db
 
 
-CompDBEntry = namedtuple('CompDBEntry', ['directory', 'filename', 'command'])
-
 def _PruneGitFiles(git_files, paths):
   """Prunes the list of files from git to include only those that are either in
   |paths| or start with one item in |paths|.
@@ -136,22 +132,14 @@
   return files
 
 
-def _GetEntriesFromCompileDB(build_directory, source_filenames):
-  """ Gets the list of files and args mentioned in the compilation database.
+def _GetFilesFromCompileDB(build_directory):
+  """ Gets the list of files mentioned in the compilation database.
 
   Args:
     build_directory: Directory that contains the compile database.
-    source_filenames: If not None, only include entries for the given list of
-      filenames.
   """
-
-  filenames_set = None if source_filenames is None else set(source_filenames)
-  return [
-      CompDBEntry(entry['directory'], entry['file'], entry['command'])
-      for entry in compile_db.Read(build_directory)
-      if filenames_set is None or os.path.realpath(
-          os.path.join(entry['directory'], entry['file'])) in filenames_set
-  ]
+  return [os.path.join(entry['directory'], entry['file'])
+          for entry in compile_db.Read(build_directory)]
 
 
 def _UpdateCompileCommandsIfNeeded(compile_commands, files_list):
@@ -179,7 +167,7 @@
   return compile_db.ProcessCompileDatabaseIfNeeded(filtered_compile_commands)
 
 
-def _ExecuteTool(toolname, tool_args, build_directory, compdb_entry):
+def _ExecuteTool(toolname, tool_args, build_directory, filename):
   """Executes the clang tool.
 
   This is defined outside the class so it can be pickled for the multiprocessing
@@ -189,7 +177,7 @@
     toolname: Name of the clang tool to execute.
     tool_args: Arguments to be passed to the clang tool. Can be None.
     build_directory: Directory that contains the compile database.
-    compdb_entry: The file and args to run the clang tool over.
+    filename: The file to run the clang tool over.
 
   Returns:
     A dictionary that must contain the key "status" and a boolean value
@@ -201,59 +189,38 @@
     Otherwise, the filename and the output from stderr are associated with the
     keys "filename" and "stderr_text" respectively.
   """
-
-  args = [toolname, compdb_entry.filename]
+  args = [toolname, '-p', build_directory, filename]
   if (tool_args):
     args.extend(tool_args)
-
-  args.append('--')
-  args.extend([
-      a for a in shlex.split(compdb_entry.command)
-      # 'command' contains the full command line, including the input
-      # source file itself. We need to filter it out otherwise it's
-      # passed to the tool twice - once directly and once via
-      # the compile args.
-      if a != compdb_entry.filename
-  ])
-
   command = subprocess.Popen(
-      args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=build_directory)
+      args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   stdout_text, stderr_text = command.communicate()
   stderr_text = re.sub(
       r"^warning: .*'linker' input unused \[-Wunused-command-line-argument\]\n",
       "", stderr_text, flags=re.MULTILINE)
-
   if command.returncode != 0:
-    return {
-        'status': False,
-        'filename': compdb_entry.filename,
-        'stderr_text': stderr_text,
-    }
+    return {'status': False, 'filename': filename, 'stderr_text': stderr_text}
   else:
-    return {
-        'status': True,
-        'filename': compdb_entry.filename,
-        'stdout_text': stdout_text,
-        'stderr_text': stderr_text,
-    }
+    return {'status': True, 'filename': filename, 'stdout_text': stdout_text,
+            'stderr_text': stderr_text}
 
 
 class _CompilerDispatcher(object):
   """Multiprocessing controller for running clang tools in parallel."""
 
-  def __init__(self, toolname, tool_args, build_directory, compdb_entries):
+  def __init__(self, toolname, tool_args, build_directory, filenames):
     """Initializer method.
 
     Args:
       toolname: Path to the tool to execute.
       tool_args: Arguments to be passed to the tool. Can be None.
       build_directory: Directory that contains the compile database.
-      compdb_entries: The files and args to run the tool over.
+      filenames: The files to run the tool over.
     """
     self.__toolname = toolname
     self.__tool_args = tool_args
     self.__build_directory = build_directory
-    self.__compdb_entries = compdb_entries
+    self.__filenames = filenames
     self.__success_count = 0
     self.__failed_count = 0
 
@@ -267,7 +234,7 @@
     result_iterator = pool.imap_unordered(
         functools.partial(_ExecuteTool, self.__toolname, self.__tool_args,
                           self.__build_directory),
-                          self.__compdb_entries)
+                          self.__filenames)
     for result in result_iterator:
       self.__ProcessResult(result)
     sys.stderr.write('\n')
@@ -288,7 +255,7 @@
       sys.stderr.write(result['stderr_text'])
       sys.stderr.write('\n')
     done_count = self.__success_count + self.__failed_count
-    percentage = (float(done_count) / len(self.__compdb_entries)) * 100
+    percentage = (float(done_count) / len(self.__filenames)) * 100
     sys.stderr.write(
         'Processed %d files with %s tool (%d failures) [%.2f%%]\r' %
         (done_count, self.__toolname, self.__failed_count, percentage))
@@ -354,25 +321,26 @@
     with open(os.path.join(args.p, 'compile_commands.json'), 'w') as f:
       f.write(json.dumps(compile_commands, indent=2))
 
-  compdb_entries = set(_GetEntriesFromCompileDB(args.p, source_filenames))
+  if args.all:
+    source_filenames = set(_GetFilesFromCompileDB(args.p))
 
   if args.shard:
-    total_length = len(compdb_entries)
+    total_length = len(source_filenames)
     match = re.match(r'(\d+)-of-(\d+)$', args.shard)
     # Input is 1-based, but modular arithmetic is 0-based.
     shard_number = int(match.group(1)) - 1
     shard_count = int(match.group(2))
-    compdb_entries = [
-        f for i, f in enumerate(sorted(compdb_entries))
-        if i % shard_count == shard_number
+    source_filenames = [
+        f[1] for f in enumerate(sorted(source_filenames))
+        if f[0] % shard_count == shard_number
     ]
     print 'Shard %d-of-%d will process %d entries out of %d' % (
-        shard_number, shard_count, len(compdb_entries), total_length)
+        shard_number, shard_count, len(source_filenames), total_length)
 
   dispatcher = _CompilerDispatcher(os.path.join(tool_path, args.tool),
                                    args.tool_arg,
                                    args.p,
-                                   compdb_entries)
+                                   source_filenames)
   dispatcher.Run()
   return -dispatcher.failed_count
 
diff --git a/tools/code_coverage/coverage.py b/tools/code_coverage/coverage.py
index 5a707cf6..962f8515 100755
--- a/tools/code_coverage/coverage.py
+++ b/tools/code_coverage/coverage.py
@@ -213,6 +213,8 @@
     self._header_template = jinja_env.get_template('header.html')
     self._table_template = jinja_env.get_template('table.html')
     self._footer_template = jinja_env.get_template('footer.html')
+    self._style_overrides = open(
+        os.path.join(template_dir, 'style_overrides.css')).read()
 
   def AddLinkToAnotherReport(self, html_report_path, name, summary):
     """Adds a link to another html report in this report.
@@ -312,7 +314,8 @@
         component_view_href=_GetRelativePathToDirectoryOfFile(
             component_view_path, self._output_path),
         file_view_href=_GetRelativePathToDirectoryOfFile(
-            file_view_path, self._output_path))
+            file_view_path, self._output_path),
+        style_overrides=self._style_overrides)
 
     html_table = self._table_template.render(
         entries=self._table_entries,
diff --git a/tools/code_coverage/html_templates/header.html b/tools/code_coverage/html_templates/header.html
index 7696ca3..fedf0c8 100644
--- a/tools/code_coverage/html_templates/header.html
+++ b/tools/code_coverage/html_templates/header.html
@@ -4,13 +4,14 @@
     <meta name='viewport' content='width=device-width,initial-scale=1'>
     <meta charset='UTF-8'>
     <link rel='stylesheet' type='text/css' href='{{ css_path }}'>
+    <!-- Custom style overrides -->
+    <style>
+      {{ style_overrides }}
+    </style>
   </head>
   <body>
     <h2>Coverage Report</h2>
     <p>
-      Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCoverage.html#interpreting-reports'>here</a> for information about interpreting this report.
-    </p>
-    <p>
-      View results by: <a href='{{ directory_view_href }}'>Directories</a> | <a href='{{ component_view_href }}'>Components</a> | <a href='{{ file_view_href }}'>Files</a>
+      View results by: <a href='{{ component_view_href }}'>Components</a> | <a href='{{ directory_view_href }}'>Directories</a> | <a href='{{ file_view_href }}'>Files</a>
     </p>
     <div class='centered'>
\ No newline at end of file
diff --git a/tools/code_coverage/html_templates/style_overrides.css b/tools/code_coverage/html_templates/style_overrides.css
new file mode 100644
index 0000000..e59c22a
--- /dev/null
+++ b/tools/code_coverage/html_templates/style_overrides.css
@@ -0,0 +1,28 @@
+/* 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. */
+
+th, td {
+  padding: 5px 16px;
+}
+
+tbody td, tfoot td {
+  font-size: 14px;
+}
+
+thead th {
+  font-size: 16px;
+}
+
+thead th, tfoot td {
+  background-color: #f5f5f5;
+  border-left: 1px solid #e4e4e4;
+  border-right: 1px solid #e4e4e4;
+  color: #484848;
+  text-transform: uppercase;
+}
+
+tbody tr:hover td, tfoot tr:hover td {
+  background-color: #f5f5f5;
+  border-color: #e4e4e4 !important;
+}
\ No newline at end of file
diff --git a/tools/code_coverage/html_templates/table.html b/tools/code_coverage/html_templates/table.html
index 226b29d..28f88952 100644
--- a/tools/code_coverage/html_templates/table.html
+++ b/tools/code_coverage/html_templates/table.html
@@ -1,34 +1,46 @@
 <table>
-  <tr>
-    <td class="column-entry-bold">{{ table_entry_type }}</td>
-    <td class="column-entry-bold">Line Coverage</td>
-    <td class="column-entry-bold">Function Coverage</td>
-    <td class="column-entry-bold">Region Coverage</td>
-  </tr>
+  <thead>
+    <tr>
+      <th class="column-entry-bold">{{ table_entry_type }}</th>
+      <th class="column-entry-bold" title=
+          "Line coverage is the percentage of code lines which have been executed at least once. Only executable lines within function bodies are considered to be code lines.">
+        Line Coverage</th>
+      <th class="column-entry-bold" title=
+          "Function coverage is the percentage of functions which have been executed at least once. A function is considered to be executed if any of its instantiations are executed.">
+        Function Coverage</th>
+      <th class="column-entry-bold" title=
+          "Region coverage is the percentage of code regions which have been executed at least once. A code region may span multiple lines (e.g in a large function body with no control flow). However, it's also possible for a single line to contain multiple code regions (e.g in 'return x || y &amp;&amp; z').">
+        Region Coverage</th>
+    </tr>
+  </thead>
+  <tbody>
   {% for entry in entries %}
-  <tr class="light-row">
-    <td>
-      {% if entry["is_dir"] == True %}
-        <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}/</a></pre>
-      {% else %}
-        <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}</a></pre>
-      {% endif %}
-    </td>
-    {% for feature in ("lines", "functions", "regions") %}
-    <td class='column-entry-{{ entry[feature]["color_class"] }}'>
-      <pre>{{ entry[feature]["percentage"] }}% ({{ entry[feature]["covered"] }}/{{ entry[feature]["total"] }})</pre>
-    </td>
+    <tr class="light-row">
+      <td>
+        {% if entry["is_dir"] == True %}
+          <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}/</a></pre>
+        {% else %}
+          <pre><a href='{{ entry["href"] }}'>{{ entry["name"] }}</a></pre>
+        {% endif %}
+      </td>
+      {% for feature in ("lines", "functions", "regions") %}
+      <td class='column-entry-{{ entry[feature]["color_class"] }}'>
+        <pre>{{ entry[feature]["percentage"] }}% ({{ entry[feature]["covered"] }}/{{ entry[feature]["total"] }})</pre>
+      </td>
+      {% endfor %}
+    </tr>
     {% endfor %}
-  </tr>
-  {% endfor %}
-  <tr class="light-row-bold">
-    <td>
-      <pre>Totals</pre>
-    </td>
-    {% for feature in ("lines", "functions", "regions") %}
-    <td class='column-entry-{{ total_entry[feature]["color_class"] }}'>
-      <pre>{{ total_entry[feature]["percentage"] }}% ({{ total_entry[feature]["covered"] }}/{{ total_entry[feature]["total"] }})</pre>
-    </td>
-    {% endfor %}
-  </tr>
+  </tbody>
+  <tfoot>
+    <tr class="light-row-bold">
+      <td>
+        <pre>Totals</pre>
+      </td>
+      {% for feature in ("lines", "functions", "regions") %}
+      <td class='column-entry-{{ total_entry[feature]["color_class"] }}'>
+        <pre>{{ total_entry[feature]["percentage"] }}% ({{ total_entry[feature]["covered"] }}/{{ total_entry[feature]["total"] }})</pre>
+      </td>
+      {% endfor %}
+    </tr>
+  </tfoot>
 </table>
\ No newline at end of file
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 69223f2a..7e838ac9 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -406,6 +406,10 @@
     "includes": [28750],
   },
 
+  "chromecast/renderer/resources/extensions_renderer_resources.grd": {
+    "includes": [28900],
+  },
+
   # END "everything else" section.
   # Everything but chrome/, components/, content/, and ios/
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 04229a4..cea0cb3 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -56,6 +56,7 @@
       'Deterministic Android (dbg)': 'android_debug_bot',
       'KitKat Phone Tester (rel)': 'android_release_bot_minimal_symbols',
       'Marshmallow Phone Tester (rel)': 'android_release_bot_minimal_symbols_arm64',
+      'android-kitkat-arm-rel': 'android_release_bot_minimal_symbols',
     },
 
     'chromium.android.fyi': {
@@ -320,28 +321,22 @@
     },
 
     'chromium.linux': {
-      'Android Arm64 Builder (dbg)': 'android_debug_static_bot_arm64',
-      'Android Builder (dbg)': 'android_debug_static_bot',
-      'Android Builder': 'android_release_bot_minimal_symbols',
-      'Android Clang Builder (dbg)': 'android_clang_asan_debug_bot_minimal_symbols',
-      'Android Tests (dbg)': 'android_debug_static_bot',
-      'Android Tests': 'android_release_bot_minimal_symbols',
-      'Cast Android (dbg)': 'android_cast_debug_static_bot',
-      'Cast Linux': 'cast_release_bot',
       'Cast Audio Linux': 'cast_audio_release_bot',
-      'linux-jumbo-rel': 'jumbo_release_bot_minimal_symbols',
-      'Leak Detection Linux': 'release_bot',
-      'Linux Builder (dbg)': 'debug_bot',
-      'Linux Builder (dbg)(32)': 'debug_bot_x86',
-      'Linux Builder': 'release_bot',
-      'Deterministic Linux': 'release_bot',
+      'Cast Linux': 'cast_release_bot',
       'Deterministic Linux (dbg)': 'debug_bot',
+      'Deterministic Linux': 'release_bot',
       'Fuchsia ARM64 Cast Audio': 'release_bot_fuchsia_arm64_cast_audio',
       'Fuchsia ARM64': 'release_bot_fuchsia_arm64',
       'Fuchsia x64 Cast Audio': 'release_bot_fuchsia_cast_audio',
       'Fuchsia x64': 'release_bot_fuchsia',
+      'Leak Detection Linux': 'release_bot',
+      'Linux Builder (dbg)': 'debug_bot',
+      'Linux Builder (dbg)(32)': 'debug_bot_x86',
+      'Linux Builder': 'release_bot',
       'linux-gcc-rel': 'release_bot_x86_minimal_symbols_no_clang_cxx11',
+      'linux-jumbo-rel': 'jumbo_release_bot_minimal_symbols',
       'linux-ozone-rel': 'ozone_linux_release_bot',
+      'linux-xenial-rel': 'release_bot',
     },
 
     'chromium.lkgr': {
@@ -538,6 +533,7 @@
     },
 
     'tryserver.chromium.android': {
+      'android-kitkat-arm-rel': 'android_release_trybot',
       'android_archive_rel_ng': 'android_release_trybot',
       'android_arm64_dbg_recipe': 'android_debug_trybot_compile_only_arm64',
       'android_blink_rel': 'android_release_trybot',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 056b0424..3b24fa2 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -18043,6 +18043,12 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="StatusArea_Audio_Muted">
+  <owner>shihuis@chromium.org</owner>
+  <owner>tetsui@chromium.org</owner>
+  <description>User muted audio from the button in SystemTray.</description>
+</action>
+
 <action name="StatusArea_Audio_SwitchInputDevice">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
@@ -18053,6 +18059,12 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="StatusArea_Audio_Unmuted">
+  <owner>shihuis@chromium.org</owner>
+  <owner>tetsui@chromium.org</owner>
+  <description>User unmuted audio from the button in SystemTray.</description>
+</action>
+
 <action name="StatusArea_AutoClickDisabled">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 969b1e74..d483fd79 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2780,8 +2780,8 @@
 </enum>
 
 <enum name="AVDAFrameInformation">
-  <int value="0" label="SurfaceTexture Insecure"/>
-  <int value="1" label="SurfaceTexture L3"/>
+  <int value="0" label="Non Overlay Insecure"/>
+  <int value="1" label="Non Overlay L3"/>
   <int value="2" label="Overlay L3"/>
   <int value="3" label="Overlay L1"/>
   <int value="4" label="Overlay Insecure Player-element Fullscreen"/>
@@ -6226,6 +6226,9 @@
 </enum>
 
 <enum name="ColorSpaceNonlinearFitConverged">
+  <obsolete>
+    Nonlinear fit code moved to skcms on 2018-04-25.
+  </obsolete>
   <int value="0" label="Did not converge."/>
   <int value="1" label="Converged."/>
 </enum>
@@ -8131,6 +8134,12 @@
   <int value="1" label="Ctrl + S to read selection"/>
 </enum>
 
+<enum name="CrosSelectToSpeakStateChangeEvent">
+  <int value="0" label="Start selection"/>
+  <int value="1" label="Cancel speech"/>
+  <int value="2" label="Cancel selection"/>
+</enum>
+
 <enum name="CrosShelfClickTarget">
   <obsolete>
     Deprecated as of 12/2013. Default pinned apps trial is finished.
@@ -8173,6 +8182,11 @@
   <int value="2" label="The system clock is known to be inaccurate"/>
 </enum>
 
+<enum name="CrosSystemTrayInteraction">
+  <int value="0" label="Tap"/>
+  <int value="1" label="Click"/>
+</enum>
+
 <enum name="CrostiniAppLaunchAppType">
   <int value="0" label="Unknown App"/>
   <int value="1" label="Terminal"/>
@@ -15386,6 +15400,8 @@
   <int value="1247" label="WALLPAPERPRIVATE_GETCURRENTWALLPAPERTHUMBNAIL"/>
   <int value="1248" label="ACCESSIBILITY_PRIVATE_ONSELECTTOSPEAKSTATECHANGED"/>
   <int value="1249" label="INPUTMETHODPRIVATE_GETCOMPOSITIONBOUNDS"/>
+  <int value="1250" label="FILEMANAGERPRIVATE_ISCROSTINIENABLED"/>
+  <int value="1251" label="FILEMANAGERPRIVATE_MOUNTCROSTINICONTAINER"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -23054,23 +23070,30 @@
 
 <enum name="ICCProfileAnalyzeResult">
   <int value="0"
-      label="Extracted primary matrix and numerical transfer function"/>
+      label="Extracted primary matrix and numerical transfer function
+             (Obsolete)"/>
   <int value="1"
-      label="Extracted primary matrix and approximated transfer function"/>
+      label="Extracted primary matrix and approximated transfer function
+             (Obsolete)"/>
   <int value="2"
-      label="Failed to converge in approximation of transfer function"/>
-  <int value="3" label="Failed to extract transfer function"/>
-  <int value="4" label="Failed to extract primary matrix"/>
+      label="Failed to converge in approximation of transfer function
+             (Obsolete)"/>
+  <int value="3" label="Failed to extract transfer function (Obsolete)"/>
+  <int value="4" label="Failed to extract primary matrix (Obsolete)"/>
   <int value="5" label="Failed to parse"/>
-  <int value="6" label="Parsed, but failed to extract SkColorSpace"/>
+  <int value="6" label="Parsed, but failed to extract SkColorSpace (Obsolete)"/>
   <int value="7"
       label="Parsed and extracteed SkColorSpace, but failed to create
-             SkColorSpaceXform to this space"/>
+             SkColorSpaceXform to this space (Obsolete)"/>
   <int value="8"
       label="Converged to an insufficiently accurate approximation of
-             transfer function"/>
-  <int value="9" label="Extracted an sRGB profile directly"/>
+             transfer function (Obsolete)"/>
+  <int value="9" label="Extracted an sRGB profile directly (Obsolete)"/>
   <int value="10" label="No profile specified"/>
+  <int value="11" label="Parsed, but failed to make a usable approximation"/>
+  <int value="12"
+      label="Got an exact color space (or accurate approximation) from the
+             profile"/>
 </enum>
 
 <enum name="IceCandidatePairTypes">
@@ -27666,6 +27689,7 @@
   <int value="963671232" label="DrawOcclusion:disabled"/>
   <int value="972228058" label="SyncUSSSessions:disabled"/>
   <int value="975104092" label="show-taps"/>
+  <int value="976079108" label="TouchpadOverscrollHistoryNavigation:disabled"/>
   <int value="979445973" label="OmniboxSpareRenderer:enabled"/>
   <int value="980396200" label="enable-new-korean-ime"/>
   <int value="981818901" label="AppBanners:enabled"/>
@@ -27825,6 +27849,7 @@
   <int value="1292763218" label="SystemTrayUnified:disabled"/>
   <int value="1294131571" label="disable-winrt-midi-api"/>
   <int value="1296958520" label="hide-active-apps-from-shelf"/>
+  <int value="1298927156" label="TouchpadOverscrollHistoryNavigation:enabled"/>
   <int value="1298981651" label="disable-new-task-manager"/>
   <int value="1300282719" label="OfflinePagesBackgroundLoading:enabled"/>
   <int value="1302421166" label="NativeNotifications:disabled"/>
@@ -34306,6 +34331,15 @@
   <int value="7" label="FAIL_DIFFERENT_PROXY_LIST"/>
 </enum>
 
+<enum name="PacUrlScheme">
+  <int value="0" label="Other"/>
+  <int value="1" label="Http"/>
+  <int value="2" label="Https"/>
+  <int value="3" label="Ftp"/>
+  <int value="4" label="File"/>
+  <int value="5" label="Data"/>
+</enum>
+
 <enum name="PageEndReason">
   <int value="0" label="END_NONE">
     Page lifetime has not yet ended (page is still active)
@@ -36490,6 +36524,33 @@
   <int value="7" label="Overflow"/>
 </enum>
 
+<enum name="PostOperationState">
+  <int value="0" label="Succeeded">The operation succeeded</int>
+  <int value="1" label="Failed: file not found">
+    The operation failed and the file operated on is not found.
+  </int>
+  <int value="2" label="Failed: path not found">
+    The operation failed and the path to the file operated on is not found.
+  </int>
+  <int value="3" label="Failed: access denied">
+    The operation failed and the process does not have permission to read the
+    attributes of the path.
+  </int>
+  <int value="4" label="Failed: cannot get attributes">
+    The operation failed as did a subsequent call to GetFileAttributes.
+  </int>
+  <int value="5" label="Failed: empty directory">
+    The operation failed leaving behind an empty directory.
+  </int>
+  <int value="6" label="Failed: non-empty directory">
+    The operation failed leaving behind a non-empty directory.
+  </int>
+  <int value="7" label="Failed: not a directory">
+    The operation failed leaving behind something other than a directory
+    (perhaps a simple file).
+  </int>
+</enum>
+
 <enum name="PostSubmitNavigation">
   <int value="0" label="Same domain"/>
   <int value="1" label="Different domain"/>
@@ -37246,6 +37307,7 @@
   <int value="8" label="PRINT_WITH_CLOUD_PRINT"/>
   <int value="9" label="PRINT_WITH_PRIVET"/>
   <int value="10" label="PRINT_WITH_EXTENSION"/>
+  <int value="11" label="OPEN_IN_MAC_PREVIEW"/>
 </enum>
 
 <enum name="PrintSettings">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6402037..ed6fcae6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -269,6 +269,18 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.CrosSelectToSpeak.StateChangeEvent"
+    enum="CrosSelectToSpeakStateChangeEvent">
+  <owner>katie@chromium.org</owner>
+  <summary>
+    A user has tapped a button in the tray to change Select-to-Speak's state.
+    The tap was interpreted by Select-to-Speak as a request to start selection,
+    to cancel speech, or to cancel selection, depending on Select-to-Speak's
+    internal state when the tap occured. This tracks when the button was tapped
+    and the event that it generated.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.CrosSelectToSpeak.WordHighlighting"
     enum="BooleanEnabled">
   <owner>katie@chromium.org</owner>
@@ -1979,6 +1991,37 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.Startup.CreationTime.Stage1.FactoryInit"
+    units="ms">
+  <owner>changwan@chromium.org</owner>
+  <summary>
+    How long it takes to initialize a WebViewChromiumFactoryProvider. This is
+    the first major phase of the WebViewChromium construction.
+  </summary>
+</histogram>
+
+<histogram name="Android.WebView.Startup.CreationTime.Stage2.ProviderInit.Cold"
+    units="ms">
+  <owner>changwan@chromium.org</owner>
+  <summary>
+    How long it takes to initialize a WebViewProvider, the first time that one
+    is initialized. WebViewProvider initialization is the second major phase of
+    WebViewChromium construction. The first initialization is recorded
+    separately because it is usually much slower than subsequent ones.
+  </summary>
+</histogram>
+
+<histogram name="Android.WebView.Startup.CreationTime.Stage2.ProviderInit.Warm"
+    units="ms">
+  <owner>changwan@chromium.org</owner>
+  <summary>
+    How long it takes to initialize a WebViewProvider, the first time that one
+    is initialized. WebViewProvider initialization is the second major phase of
+    WebViewChromium construction. When it is not the first time, it is faster
+    and thus recorded separately.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.TargetSdkVersion" enum="AndroidApiLevel">
   <owner>changwan@chromium.org</owner>
   <summary>
@@ -7494,6 +7537,9 @@
 
 <histogram name="Blink.ColorSpace.Destination.NonlinearFitConverged"
     enum="ColorSpaceNonlinearFitConverged">
+  <obsolete>
+    Nonlinear fit code moved to skcms on 2018-04-25.
+  </obsolete>
   <owner>ccameron@chromium.org</owner>
   <summary>
     Whether or not the nonlinear least squares fit of the table-based ICC
@@ -7502,6 +7548,9 @@
 </histogram>
 
 <histogram name="Blink.ColorSpace.Destination.NonlinearFitError">
+  <obsolete>
+    Nonlinear fit code moved to skcms on 2018-04-25.
+  </obsolete>
   <owner>ccameron@chromium.org</owner>
   <summary>
     The L-infinity error (in 8-bit values) of the numerical approximation of
@@ -10693,6 +10742,16 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.SystemTray.Interaction"
+    enum="CrosSystemTrayInteraction">
+  <owner>tetsui@chromium.org</owner>
+  <summary>
+    An enum value how system tray bubble is interacted e.g. by tap (touch
+    screen), or click (mouse, trackpad, etc.) Reported every time the region
+    inside system tray bubble is tapped or clicked.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.SystemTray.TimeToClick" units="ms">
   <owner>tetsui@chromium.org</owner>
   <summary>
@@ -29752,6 +29811,11 @@
   <summary>Whether or not a frame displays an overlay.</summary>
 </histogram>
 
+<histogram name="GPU.DirectComposition.SwapBuffersLastError" enum="Hresult">
+  <owner>sunnyps@chromium.org</owner>
+  <summary>Last error when presentation using DirectComposition fails.</summary>
+</histogram>
+
 <histogram name="GPU.DirectComposition.SwapchainFormat" enum="SwapchainFormat">
   <owner>jbauman@chromium.org</owner>
   <summary>What type of swapchain was actually created for an overlay.</summary>
@@ -36563,7 +36627,7 @@
   <owner>liberato@chromium.org</owner>
   <summary>
     Record a count for each frame sent to the client by AVDA, separated by what
-    type of frame it is: SurfaceTexture, secure overlay, etc.
+    type of frame it is: Non Overlay, secure overlay, etc.
   </summary>
 </histogram>
 
@@ -36572,7 +36636,7 @@
   <summary>
     Record a count for each frame sent to the client by AVDA.  True counts
     indicate that the frame was an overlay (SurfaceView).  False counts are for
-    SurfaceTexture frames.  This will be deprecated in favor of
+    Non Overlay frames.  This will be deprecated in favor of
     Media.AVDA.FrameInformation in M63.
   </summary>
 </histogram>
@@ -36626,7 +36690,7 @@
 <histogram name="Media.AvdaCodecImage.WaitTimeForFrame" units="ms">
   <owner>liberato@chromium.org</owner>
   <summary>
-    Time spent waiting for a frame to become available in a SurfaceTexture after
+    Time spent waiting for a frame to become available in a Non Overlay after
     requesting that MediaCodec renders it.
   </summary>
 </histogram>
@@ -48344,6 +48408,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.ProxyResolutionService.PacUrlScheme" enum="PacUrlScheme">
+  <owner>eroman@chromium.org</owner>
+  <summary>
+    The breakdown of URL schemes seen for explicitly configured ProxyAutoConfig
+    (PAC). This metric is emitted once each time the proxy settings change
+    (including initial value). Note that this does not count implicitly inferred
+    PAC URLs (from WPAD).
+  </summary>
+</histogram>
+
 <histogram name="Net.ProxyResolver.AbandonedExecutionTotalTime" units="ms">
   <obsolete>
     Removed in Chrome 39.
@@ -60442,7 +60516,7 @@
   <summary>
     Time it takes for the omnibox to become responsive to user input after the
     user has typed N characters. This measures the time it takes to start all
-    the asynchronous autocomplete providers (but not wait for them to finish).
+    autocomplete providers (but not wait for the asynchronous ones to finish).
   </summary>
 </histogram>
 
@@ -72462,6 +72536,14 @@
   </summary>
 </histogram>
 
+<histogram name="PrintPreview.PageCount.OpenInMacPreview">
+  <owner>thestig@chromium.org</owner>
+  <summary>
+    The final page count (after page selection) of documents printed to PDF and
+    opened in Preview.app on Mac.
+  </summary>
+</histogram>
+
 <histogram name="PrintPreview.PageCount.PrintToCloudPrint">
   <owner>vitalybuka@chromium.org</owner>
   <summary>
@@ -72532,6 +72614,20 @@
   <summary>Print preview events.</summary>
 </histogram>
 
+<histogram name="PrintPreview.PrintDocumentSize.HTML" units="KB">
+  <owner>thestig@chromium.org</owner>
+  <summary>
+    The average size of a page in the printed document when the source is HTML.
+  </summary>
+</histogram>
+
+<histogram name="PrintPreview.PrintDocumentSize.PDF" units="KB">
+  <owner>thestig@chromium.org</owner>
+  <summary>
+    The average size of a page in the printed document when the source is PDF.
+  </summary>
+</histogram>
+
 <histogram name="PrintPreview.PrintDocumentType"
     enum="PrintPreviewPrintDocumentTypeBuckets">
   <owner>rbpotter@chromium.org</owner>
@@ -107104,6 +107200,14 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Windows.FilesystemError" enum="WinGetLastError">
+  <owner>grt@chromium.org</owner>
+  <summary>
+    The Windows error code relating to a failed attempt to operate on a file or
+    a directory.
+  </summary>
+</histogram>
+
 <histogram name="Windows.GetVersionExVersion" enum="WindowsVersion">
   <owner>scottmg@chromium.org</owner>
   <summary>
@@ -107181,6 +107285,14 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Windows.PostOperationState"
+    enum="PostOperationState">
+  <owner>grt@chromium.org</owner>
+  <summary>
+    The state of an item in the filesystem following an operation on it.
+  </summary>
+</histogram>
+
 <histogram name="Windows.Tablet" enum="BooleanTablet">
   <owner>zturner@chromium.org</owner>
   <summary>Count of browser launches from a Windows tablet pc.</summary>
@@ -110590,6 +110702,13 @@
   <affected-histogram name="FileBrowser.Load"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="FileOperations" separator=".">
+  <suffix name="DeleteFile.NonRecursive"/>
+  <suffix name="DeleteFile.Recursive"/>
+  <affected-histogram name="Windows.FilesystemError"/>
+  <affected-histogram name="Windows.PostOperationState"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="FirstPacketSplit" separator="_">
   <suffix name="first_packet_intact"
       label="with GET/POST headers often using only 1 packet"/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index a816d6a3..5ebec94 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -458,7 +458,12 @@
     'blink_perf.parser',
     'blink_perf.shadow_dom',
     'blink_perf.svg',
-    'memory.top_10_mobile'
+    'memory.top_10_mobile',
+    'system_health.common_desktop',
+    'system_health.common_mobile',
+    'system_health.memory_desktop',
+    'system_health.memory_mobile',
+    'system_health.webview_startup',
 ]
 
 
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index e2276fe..034cda31 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -208,6 +208,7 @@
 crbug.com/755556 [ Mac ] smoothness.tough_animation_cases/mix_blend_mode_animation_hue.html [ Skip ]
 crbug.com/829499 [ Android_One ] smoothness.tough_animation_cases/css_animations_many_keyframes.html?N=0316 [ Skip ]
 crbug.com/829499 [ Android_One ] smoothness.tough_animation_cases/web_animations_many_keyframes.html?N=0316 [ Skip ]
+crbug.com/840964 [ Nexus_5X ] smoothness.tough_animation_cases/web_animations_many_keyframes.html?N=0316 [ Skip ]
 
 # Benchmark: smoothness.tough_canvas_cases
 crbug.com/785485 [ Android_Webview ] smoothness.tough_canvas_cases/http://www.kevs3d.co.uk/dev/canvask3d/k3d_test.html [ Skip ]
@@ -421,6 +422,8 @@
 [ Android_One ] wasm/WasmSpaceBuggy [ Skip ]
 crbug.com/814012 [ Mac ] wasm/AsmJsZenGarden [ Skip ]
 crbug.com/814012 [ Win ] wasm/AsmJsZenGarden [ Skip ]
+crbug.com/840935 [ All ] wasm/WasmStylizedRenderer [ Skip ]
+crbug.com/840935 [ All ] wasm/WasmSunTemple [ Skip ]
 
 ##### Perf FYI benchmarks go after here #####
 # Benchmark: loading.desktop.network_service
diff --git a/tools/traffic_annotation/auditor/auditor_result.h b/tools/traffic_annotation/auditor/auditor_result.h
index 51c60d32..9d1ba078 100644
--- a/tools/traffic_annotation/auditor/auditor_result.h
+++ b/tools/traffic_annotation/auditor/auditor_result.h
@@ -67,6 +67,7 @@
   Type type() const { return type_; };
 
   std::string file_path() const { return file_path_; }
+  void set_file_path(const std::string& file_path) { file_path_ = file_path; }
 
   // Formats the error message into one line of text.
   std::string ToText() const;
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index 83df5e4..25271c58 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -100,6 +100,32 @@
   return false;
 }
 
+// If normalized |file_path| starts with |base_directory|, returns the
+// relative path to |file_path|, otherwise the original |file_path| is returned.
+std::string MakeRelativePath(const base::FilePath& base_directory,
+                             const std::string& file_path) {
+  DCHECK(base_directory.IsAbsolute());
+
+#if defined(OS_WIN)
+  base::FilePath converted_file_path = base::FilePath(
+      base::FilePath::StringPieceType((base::UTF8ToWide(file_path))));
+#else
+  base::FilePath converted_file_path(file_path);
+#endif
+  base::FilePath normalized_path;
+  if (base::NormalizeFilePath(converted_file_path, &normalized_path) &&
+      normalized_path.IsAbsolute()) {
+    normalized_path = normalized_path.NormalizePathSeparatorsTo('/');
+    std::string file_str = normalized_path.MaybeAsASCII();
+    std::string base_str = base_directory.MaybeAsASCII();
+    if (file_str.find(base_str) == 0) {
+      return file_str.substr(base_str.length() + 1,
+                             file_str.length() - base_str.length() - 1);
+    }
+  }
+  return file_path;
+}
+
 }  // namespace
 
 TrafficAnnotationAuditor::TrafficAnnotationAuditor(
@@ -115,6 +141,15 @@
   DCHECK(!build_path.empty());
   DCHECK(!clang_tool_path.empty());
 
+  // Get absolute source path.
+  base::FilePath original_path;
+  base::GetCurrentDirectory(&original_path);
+  base::SetCurrentDirectory(source_path_);
+  base::GetCurrentDirectory(&absolute_source_path_);
+  base::SetCurrentDirectory(original_path);
+  absolute_source_path_ = absolute_source_path_.NormalizePathSeparatorsTo('/');
+  DCHECK(absolute_source_path_.IsAbsolute());
+
   base::FilePath switches_file =
       base::MakeAbsoluteFilePath(source_path_.Append(kClangToolSwitchesPath));
   std::string file_content;
@@ -406,12 +441,16 @@
     if (block_type == "ANNOTATION") {
       AnnotationInstance new_annotation;
       result = new_annotation.Deserialize(lines, current, end_line);
-      if (IsSafeListed(new_annotation.proto.source().file(),
+      result.set_file_path(
+          MakeRelativePath(absolute_source_path_, result.file_path()));
+      if (IsSafeListed(result.file_path(),
                        AuditorException::ExceptionType::ALL)) {
         result = AuditorResult(AuditorResult::Type::RESULT_IGNORE);
       }
       switch (result.type()) {
         case AuditorResult::Type::RESULT_OK:
+          new_annotation.proto.mutable_source()->set_file(MakeRelativePath(
+              absolute_source_path_, new_annotation.proto.source().file()));
           extracted_annotations_.push_back(new_annotation);
           break;
         case AuditorResult::Type::ERROR_MISSING_TAG_USED:
@@ -432,6 +471,8 @@
     } else if (block_type == "CALL") {
       CallInstance new_call;
       result = new_call.Deserialize(lines, current, end_line);
+      new_call.file_path =
+          MakeRelativePath(absolute_source_path_, new_call.file_path);
       if (IsSafeListed(new_call.file_path,
                        AuditorException::ExceptionType::ALL)) {
         result = AuditorResult(AuditorResult::Type::RESULT_IGNORE);
@@ -441,6 +482,8 @@
     } else if (block_type == "ASSIGNMENT") {
       AssignmentInstance new_assignment;
       result = new_assignment.Deserialize(lines, current, end_line);
+      new_assignment.file_path =
+          MakeRelativePath(absolute_source_path_, new_assignment.file_path);
       if (IsSafeListed(new_assignment.file_path,
                        AuditorException::ExceptionType::ALL)) {
         result = AuditorResult(AuditorResult::Type::RESULT_IGNORE);
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
index a453036d..2ef563f 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
@@ -162,6 +162,8 @@
   const base::FilePath build_path_;
   const base::FilePath clang_tool_path_;
 
+  base::FilePath absolute_source_path_;
+
   std::vector<std::string> clang_tool_switches_;
 
   TrafficAnnotationExporter exporter_;
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
index e0731e3..6ccb370 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
@@ -349,16 +349,6 @@
   if (tool_path.empty())
     tool_path = command_line.GetProgram().DirName();
 
-  // If source path is not provided, guess it using build path or current
-  // directory.
-  if (source_path.empty()) {
-    if (build_path.empty())
-      base::GetCurrentDirectory(&source_path);
-    else
-      source_path = build_path.Append(base::FilePath::kParentDirectory)
-                        .Append(base::FilePath::kParentDirectory);
-  }
-
   // Get build directory, if it is empty issue an error.
   if (build_path.empty()) {
     LOG(ERROR)
@@ -368,6 +358,12 @@
     return 1;
   }
 
+  // If source path is not provided, guess it using build path.
+  if (source_path.empty()) {
+    source_path = build_path.Append(base::FilePath::kParentDirectory)
+                      .Append(base::FilePath::kParentDirectory);
+  }
+
   TrafficAnnotationAuditor auditor(source_path, build_path, tool_path);
 
   // Extract annotations.
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 85604a5..e716ceb 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -73,8 +73,6 @@
     "platform/ax_android_constants.h",
     "platform/ax_platform_node.cc",
     "platform/ax_platform_node.h",
-    "platform/ax_snapshot_node_android_platform.cc",
-    "platform/ax_snapshot_node_android_platform.h",
     "platform/ax_unique_id.cc",
     "platform/ax_unique_id.h",
   ]
@@ -151,6 +149,16 @@
   }
 }
 
+source_set("ax_assistant") {
+  sources = [
+    "ax_assistant_structure.cc",
+    "ax_assistant_structure.h",
+  ]
+  deps = [
+    ":accessibility",
+  ]
+}
+
 static_library("test_support") {
   testonly = true
   sources = [
diff --git a/ui/accessibility/DEPS b/ui/accessibility/DEPS
index 33a31c8..ab5cea7 100644
--- a/ui/accessibility/DEPS
+++ b/ui/accessibility/DEPS
@@ -13,5 +13,8 @@
 specific_include_rules = {
   "run_all_unittests.cc": [
     "+mojo/edk/embedder",
-  ]
+  ],
+  "ax_assistant_util.h": [
+    "+ui/accessibility/mojom",
+  ],
 }
diff --git a/ui/accessibility/platform/ax_snapshot_node_android_platform.cc b/ui/accessibility/ax_assistant_structure.cc
similarity index 84%
rename from ui/accessibility/platform/ax_snapshot_node_android_platform.cc
rename to ui/accessibility/ax_assistant_structure.cc
index 7ef8a5c..d47df20 100644
--- a/ui/accessibility/platform/ax_snapshot_node_android_platform.cc
+++ b/ui/accessibility/ax_assistant_structure.cc
@@ -1,12 +1,13 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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 "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
+#include "ui/accessibility/ax_assistant_structure.h"
 
 #include <string>
 
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -15,6 +16,7 @@
 #include "ui/accessibility/ax_serializable_tree.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
 #include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/range/range.h"
 #include "ui/gfx/transform.h"
 
 namespace ui {
@@ -211,11 +213,11 @@
     }
   }
 
-  if (text.empty() && (AXSnapshotNodeAndroid::AXRoleIsLink(node->data().role) ||
+  if (text.empty() && (AXRoleIsLink(node->data().role) ||
                        node->data().role == ax::mojom::Role::kImage)) {
     base::string16 url =
         node->data().GetString16Attribute(ax::mojom::StringAttribute::kUrl);
-    text = AXSnapshotNodeAndroid::AXUrlBaseText(url);
+    text = AXUrlBaseText(url);
   }
   return text;
 }
@@ -264,36 +266,135 @@
   }
 }
 
-}  // namespace
+AssistantNode* AddChild(AssistantTree* tree) {
+  auto node = std::make_unique<AssistantNode>();
+  tree->nodes.push_back(std::move(node));
+  return tree->nodes.back().get();
+}
 
-AXSnapshotNodeAndroid::AXSnapshotNodeAndroid() = default;
-AX_EXPORT AXSnapshotNodeAndroid::~AXSnapshotNodeAndroid() = default;
+struct WalkAXTreeConfig {
+  bool should_select_leaf;
+  const bool show_password;
+};
 
-// static
-AX_EXPORT std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::Create(
-    const AXTreeUpdate& update,
-    bool show_password) {
-  auto tree = std::make_unique<AXSerializableTree>();
-  if (!tree->Unserialize(update)) {
-    LOG(FATAL) << tree->error();
+void WalkAXTreeDepthFirst(const AXNode* node,
+                          const gfx::Rect& rect,
+                          const AXTreeUpdate& update,
+                          const AXTree* tree,
+                          WalkAXTreeConfig* config,
+                          AssistantTree* assistant_tree,
+                          AssistantNode* result) {
+  result->text = GetText(node, config->show_password);
+  result->class_name =
+      AXRoleToAndroidClassName(node->data().role, node->parent() != nullptr);
+  result->role = AXRoleToString(node->data().role);
+
+  result->text_size = -1.0;
+  result->bgcolor = 0;
+  result->color = 0;
+  result->bold = 0;
+  result->italic = 0;
+  result->line_through = 0;
+  result->underline = 0;
+
+  if (node->data().HasFloatAttribute(ax::mojom::FloatAttribute::kFontSize)) {
+    gfx::RectF text_size_rect(
+        0, 0, 1,
+        node->data().GetFloatAttribute(ax::mojom::FloatAttribute::kFontSize));
+    gfx::Rect scaled_text_size_rect =
+        gfx::ToEnclosingRect(tree->RelativeToTreeBounds(node, text_size_rect));
+    result->text_size = scaled_text_size_rect.height();
+
+    const int32_t text_style =
+        node->data().GetIntAttribute(ax::mojom::IntAttribute::kTextStyle);
+    result->color =
+        node->data().GetIntAttribute(ax::mojom::IntAttribute::kColor);
+    result->bgcolor =
+        node->data().GetIntAttribute(ax::mojom::IntAttribute::kBackgroundColor);
+    result->bold =
+        (text_style &
+         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleBold)) != 0;
+    result->italic =
+        (text_style &
+         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleItalic)) != 0;
+    result->line_through =
+        (text_style & static_cast<int32_t>(
+                          ax::mojom::TextStyle::kTextStyleLineThrough)) != 0;
+    result->underline =
+        (text_style &
+         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleUnderline)) != 0;
   }
 
+  const gfx::Rect& absolute_rect =
+      gfx::ToEnclosingRect(tree->GetTreeBounds(node));
+  gfx::Rect parent_relative_rect = absolute_rect;
+  bool is_root = node->parent() == nullptr;
+  if (!is_root) {
+    parent_relative_rect.Offset(-rect.OffsetFromOrigin());
+  }
+  result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(),
+                           absolute_rect.width(), absolute_rect.height());
+
+  if (IsLeaf(node) && update.has_tree_data) {
+    int start_selection = 0;
+    int end_selection = 0;
+    if (update.tree_data.sel_anchor_object_id == node->id()) {
+      start_selection = update.tree_data.sel_anchor_offset;
+      config->should_select_leaf = true;
+    }
+
+    if (config->should_select_leaf) {
+      end_selection =
+          static_cast<int32_t>(GetText(node, config->show_password).length());
+    }
+
+    if (update.tree_data.sel_focus_object_id == node->id()) {
+      end_selection = update.tree_data.sel_focus_offset;
+      config->should_select_leaf = false;
+    }
+    if (end_selection > 0)
+      result->selection =
+          base::make_optional<gfx::Range>(start_selection, end_selection);
+  }
+
+  for (auto* child : node->children()) {
+    auto* n = AddChild(assistant_tree);
+    result->children_indices.push_back(assistant_tree->nodes.size() - 1);
+    WalkAXTreeDepthFirst(child, absolute_rect, update, tree, config,
+                         assistant_tree, n);
+  }
+}
+
+}  // namespace
+
+AssistantNode::AssistantNode() = default;
+AssistantNode::AssistantNode(const AssistantNode& other) = default;
+AssistantNode::~AssistantNode() = default;
+
+AssistantTree::AssistantTree() = default;
+AssistantTree::~AssistantTree() = default;
+
+std::unique_ptr<AssistantTree> CreateAssistantTree(const AXTreeUpdate& update,
+                                                   bool show_password) {
+  auto tree = std::make_unique<AXSerializableTree>();
+  auto assistant_tree = std::make_unique<AssistantTree>();
+  auto* root = AddChild(assistant_tree.get());
+  if (!tree->Unserialize(update))
+    LOG(FATAL) << tree->error();
   WalkAXTreeConfig config{
       false,         // should_select_leaf
       show_password  // show_password
   };
-  return WalkAXTreeDepthFirst(tree->root(), gfx::Rect(), update, tree.get(),
-                              config);
+  WalkAXTreeDepthFirst(tree->root(), gfx::Rect(), update, tree.get(), &config,
+                       assistant_tree.get(), root);
+  return assistant_tree;
 }
 
-// static
-AX_EXPORT bool AXSnapshotNodeAndroid::AXRoleIsLink(ax::mojom::Role role) {
+bool AXRoleIsLink(ax::mojom::Role role) {
   return role == ax::mojom::Role::kLink;
 }
 
-// static
-AX_EXPORT base::string16 AXSnapshotNodeAndroid::AXUrlBaseText(
-    base::string16 url) {
+base::string16 AXUrlBaseText(base::string16 url) {
   // Given a url like http://foo.com/bar/baz.png, just return the
   // base text, e.g., "baz".
   int trailing_slashes = 0;
@@ -312,10 +413,7 @@
   return url;
 }
 
-// static
-AX_EXPORT const char* AXSnapshotNodeAndroid::AXRoleToAndroidClassName(
-    ax::mojom::Role role,
-    bool has_parent) {
+const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent) {
   switch (role) {
     case ax::mojom::Role::kSearchBox:
     case ax::mojom::Role::kSpinButton:
@@ -370,98 +468,4 @@
   }
 }
 
-// static
-std::unique_ptr<AXSnapshotNodeAndroid>
-AXSnapshotNodeAndroid::WalkAXTreeDepthFirst(
-    const AXNode* node,
-    gfx::Rect rect,
-    const AXTreeUpdate& update,
-    const AXTree* tree,
-    AXSnapshotNodeAndroid::WalkAXTreeConfig& config) {
-  auto result =
-      std::unique_ptr<AXSnapshotNodeAndroid>(new AXSnapshotNodeAndroid());
-  result->text = GetText(node, config.show_password);
-  result->class_name = AXSnapshotNodeAndroid::AXRoleToAndroidClassName(
-      node->data().role, node->parent() != nullptr);
-  result->role = AXRoleToString(node->data().role);
-
-  result->text_size = -1.0;
-  result->bgcolor = 0;
-  result->color = 0;
-  result->bold = 0;
-  result->italic = 0;
-  result->line_through = 0;
-  result->underline = 0;
-
-  if (node->data().HasFloatAttribute(ax::mojom::FloatAttribute::kFontSize)) {
-    gfx::RectF text_size_rect(
-        0, 0, 1,
-        node->data().GetFloatAttribute(ax::mojom::FloatAttribute::kFontSize));
-    gfx::Rect scaled_text_size_rect =
-        gfx::ToEnclosingRect(tree->RelativeToTreeBounds(node, text_size_rect));
-    result->text_size = scaled_text_size_rect.height();
-
-    const int32_t text_style =
-        node->data().GetIntAttribute(ax::mojom::IntAttribute::kTextStyle);
-    result->color =
-        node->data().GetIntAttribute(ax::mojom::IntAttribute::kColor);
-    result->bgcolor =
-        node->data().GetIntAttribute(ax::mojom::IntAttribute::kBackgroundColor);
-    result->bold =
-        (text_style &
-         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleBold)) != 0;
-    result->italic =
-        (text_style &
-         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleItalic)) != 0;
-    result->line_through =
-        (text_style & static_cast<int32_t>(
-                          ax::mojom::TextStyle::kTextStyleLineThrough)) != 0;
-    result->underline =
-        (text_style &
-         static_cast<int32_t>(ax::mojom::TextStyle::kTextStyleUnderline)) != 0;
-  }
-
-  const gfx::Rect& absolute_rect =
-      gfx::ToEnclosingRect(tree->GetTreeBounds(node));
-  gfx::Rect parent_relative_rect = absolute_rect;
-  bool is_root = node->parent() == nullptr;
-  if (!is_root) {
-    parent_relative_rect.Offset(-rect.OffsetFromOrigin());
-  }
-  result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(),
-                           absolute_rect.width(), absolute_rect.height());
-  result->has_selection = false;
-
-  if (IsLeaf(node) && update.has_tree_data) {
-    int start_selection = 0;
-    int end_selection = 0;
-    if (update.tree_data.sel_anchor_object_id == node->id()) {
-      start_selection = update.tree_data.sel_anchor_offset;
-      config.should_select_leaf = true;
-    }
-
-    if (config.should_select_leaf) {
-      end_selection =
-          static_cast<int32_t>(GetText(node, config.show_password).length());
-    }
-
-    if (update.tree_data.sel_focus_object_id == node->id()) {
-      end_selection = update.tree_data.sel_focus_offset;
-      config.should_select_leaf = false;
-    }
-    if (end_selection > 0) {
-      result->has_selection = true;
-      result->start_selection = start_selection;
-      result->end_selection = end_selection;
-    }
-  }
-
-  for (auto* child : node->children()) {
-    result->children.push_back(
-        WalkAXTreeDepthFirst(child, absolute_rect, update, tree, config));
-  }
-
-  return result;
-}
-
 }  // namespace ui
diff --git a/ui/accessibility/ax_assistant_structure.h b/ui/accessibility/ax_assistant_structure.h
new file mode 100644
index 0000000..19a9cf52
--- /dev/null
+++ b/ui/accessibility/ax_assistant_structure.h
@@ -0,0 +1,75 @@
+// 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 UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
+#define UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/ax_tree_update.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/range/range.h"
+
+namespace ui {
+
+struct AssistantNode {
+  AssistantNode();
+  AssistantNode(const AssistantNode& other);
+  ~AssistantNode();
+
+  std::vector<int32_t> children_indices;
+
+  // Geometry of the view in pixels
+  gfx::Rect rect;
+
+  // Text of the view.
+  base::string16 text;
+
+  // Text properties
+  float text_size;
+  uint32_t color;
+  uint32_t bgcolor;
+  bool bold;
+  bool italic;
+  bool underline;
+  bool line_through;
+
+  // Selected portion of the text.
+  base::Optional<gfx::Range> selection;
+
+  // Fake Android view class name of the element.  Each node is assigned
+  // a closest approximation of Android's views to keep the server happy.
+  std::string class_name;
+
+  // Accessibility functionality of the node inferred from DOM or based on HTML
+  // role attribute.
+  base::Optional<std::string> role;
+};
+
+struct AssistantTree {
+  AssistantTree();
+  ~AssistantTree();
+
+  std::vector<std::unique_ptr<AssistantNode>> nodes;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AssistantTree);
+};
+
+std::unique_ptr<AssistantTree> CreateAssistantTree(const AXTreeUpdate& update,
+                                                   bool show_password);
+
+bool AXRoleIsLink(ax::mojom::Role role);
+base::string16 AXUrlBaseText(base::string16 url);
+const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent);
+
+}  // namespace ui
+
+#endif  // UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index 130d47ee..55dfa1f 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -83,7 +83,7 @@
   data_ = src;
 }
 
-void AXNode::SetLocation(int offset_container_id,
+void AXNode::SetLocation(int32_t offset_container_id,
                          const gfx::RectF& location,
                          gfx::Transform* transform) {
   data_.offset_container_id = offset_container_id;
@@ -170,4 +170,8 @@
   return base::UTF8ToUTF16(GetInheritedStringAttribute(attribute));
 }
 
+std::ostream& operator<<(std::ostream& stream, const AXNode& node) {
+  return stream << node.data().ToString();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index c832a6a..ca5aa32 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -7,14 +7,16 @@
 
 #include <stdint.h>
 
+#include <ostream>
 #include <vector>
 
+#include "ui/accessibility/ax_export.h"
 #include "ui/accessibility/ax_node_data.h"
 
 namespace ui {
 
 // One node in an AXTree.
-class AX_EXPORT AXNode {
+class AX_EXPORT AXNode final {
  public:
   // The constructor requires a parent, id, and index in parent, but
   // the data is not required. After initialization, only index_in_parent
@@ -42,18 +44,18 @@
   // Returns true if the node has any of the text related roles.
   bool IsTextNode() const;
 
-  // Set the node's accessibility data. This may be done during initial
-  // initialization or later when the node data changes.
+  // Set the node's accessibility data. This may be done during initialization
+  // or later when the node data changes.
   void SetData(const AXNodeData& src);
 
-  // Update this node's location. This is separate from SetData just because
+  // Update this node's location. This is separate from |SetData| just because
   // changing only the location is common and should be more efficient than
   // re-copying all of the data.
   //
   // The node's location is stored as a relative bounding box, the ID of
   // the element it's relative to, and an optional transformation matrix.
   // See ax_node_data.h for details.
-  void SetLocation(int offset_container_id,
+  void SetLocation(int32_t offset_container_id,
                    const gfx::RectF& location,
                    gfx::Transform* transform);
 
@@ -78,6 +80,102 @@
   // by computing them and caching the result.
   std::vector<int> GetOrComputeLineStartOffsets();
 
+  // Accessing accessibility attributes.
+  // See |AXNodeData| for more information.
+
+  constexpr bool HasBoolAttribute(ax::mojom::BoolAttribute attribute) const {
+    return data().HasBoolAttribute(attribute);
+  }
+  constexpr bool GetBoolAttribute(ax::mojom::BoolAttribute attribute) const {
+    return data().GetBoolAttribute(attribute);
+  }
+  constexpr bool GetBoolAttribute(ax::mojom::BoolAttribute attribute,
+                                  bool* value) const {
+    return data().GetBoolAttribute(attribute, value);
+  }
+
+  constexpr bool HasFloatAttribute(ax::mojom::FloatAttribute attribute) const {
+    return data().HasFloatAttribute(attribute);
+  }
+  constexpr float GetFloatAttribute(ax::mojom::FloatAttribute attribute) const {
+    return data().GetFloatAttribute(attribute);
+  }
+  constexpr bool GetFloatAttribute(ax::mojom::FloatAttribute attribute,
+                                   float* value) const {
+    return data().GetFloatAttribute(attribute, value);
+  }
+
+  constexpr bool HasIntAttribute(ax::mojom::IntAttribute attribute) const {
+    return data().HasIntAttribute(attribute);
+  }
+  constexpr int32_t GetIntAttribute(ax::mojom::IntAttribute attribute) const {
+    return data().GetIntAttribute(attribute);
+  }
+  constexpr bool GetIntAttribute(ax::mojom::IntAttribute attribute,
+                                 int* value) const {
+    return data().GetIntAttribute(attribute, value);
+  }
+
+  constexpr bool HasStringAttribute(
+      ax::mojom::StringAttribute attribute) const {
+    return data().HasStringAttribute(attribute);
+  }
+  constexpr const std::string& GetStringAttribute(
+      ax::mojom::StringAttribute attribute) const {
+    return data().GetStringAttribute(attribute);
+  }
+  constexpr bool GetStringAttribute(ax::mojom::StringAttribute attribute,
+                                    std::string* value) const {
+    return data().GetStringAttribute(attribute, value);
+  }
+
+  constexpr bool GetString16Attribute(ax::mojom::StringAttribute attribute,
+                                      base::string16* value) const {
+    return data().GetString16Attribute(attribute, value);
+  }
+  // Cannot be constexpr because |base::string16| doesn't have a constexpr
+  // constructor.
+  base::string16 GetString16Attribute(
+      ax::mojom::StringAttribute attribute) const {
+    return data().GetString16Attribute(attribute);
+  }
+
+  constexpr bool HasIntListAttribute(
+      ax::mojom::IntListAttribute attribute) const {
+    return data().HasIntListAttribute(attribute);
+  }
+  constexpr const std::vector<int32_t>& GetIntListAttribute(
+      ax::mojom::IntListAttribute attribute) const {
+    return data().GetIntListAttribute(attribute);
+  }
+  constexpr bool GetIntListAttribute(ax::mojom::IntListAttribute attribute,
+                                     std::vector<int32_t>* value) const {
+    return data().GetIntListAttribute(attribute, value);
+  }
+
+  constexpr bool HasStringListAttribute(
+      ax::mojom::StringListAttribute attribute) const {
+    return data().HasStringListAttribute(attribute);
+  }
+  constexpr const std::vector<std::string>& GetStringListAttribute(
+      ax::mojom::StringListAttribute attribute) const {
+    return data().GetStringListAttribute(attribute);
+  }
+  constexpr bool GetStringListAttribute(
+      ax::mojom::StringListAttribute attribute,
+      std::vector<std::string>* value) const {
+    return data().GetStringListAttribute(attribute, value);
+  }
+
+  constexpr bool GetHtmlAttribute(const char* attribute,
+                                  base::string16* value) const {
+    return data().GetHtmlAttribute(attribute, value);
+  }
+  constexpr bool GetHtmlAttribute(const char* attribute,
+                                  std::string* value) const {
+    return data().GetHtmlAttribute(attribute, value);
+  }
+
   const std::string& GetInheritedStringAttribute(
       ax::mojom::StringAttribute attribute) const;
   base::string16 GetInheritedString16Attribute(
@@ -95,6 +193,8 @@
   AXNodeData data_;
 };
 
+AX_EXPORT std::ostream& operator<<(std::ostream& stream, const AXNode& node);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_NODE_H_
diff --git a/ui/accessibility/ax_node_data.h b/ui/accessibility/ax_node_data.h
index 8d665a2..531a3c2 100644
--- a/ui/accessibility/ax_node_data.h
+++ b/ui/accessibility/ax_node_data.h
@@ -57,13 +57,14 @@
   // attribute is not present. In addition, strings can be returned as
   // either std::string or base::string16, for convenience.
 
-  bool HasBoolAttribute(ax::mojom::BoolAttribute attr) const;
-  bool GetBoolAttribute(ax::mojom::BoolAttribute attr) const;
-  bool GetBoolAttribute(ax::mojom::BoolAttribute attr, bool* value) const;
+  bool HasBoolAttribute(ax::mojom::BoolAttribute attribute) const;
+  bool GetBoolAttribute(ax::mojom::BoolAttribute attribute) const;
+  bool GetBoolAttribute(ax::mojom::BoolAttribute attribute, bool* value) const;
 
-  bool HasFloatAttribute(ax::mojom::FloatAttribute attr) const;
-  float GetFloatAttribute(ax::mojom::FloatAttribute attr) const;
-  bool GetFloatAttribute(ax::mojom::FloatAttribute attr, float* value) const;
+  bool HasFloatAttribute(ax::mojom::FloatAttribute attribute) const;
+  float GetFloatAttribute(ax::mojom::FloatAttribute attribute) const;
+  bool GetFloatAttribute(ax::mojom::FloatAttribute attribute,
+                         float* value) const;
 
   bool HasIntAttribute(ax::mojom::IntAttribute attribute) const;
   int32_t GetIntAttribute(ax::mojom::IntAttribute attribute) const;
@@ -92,8 +93,8 @@
   bool GetStringListAttribute(ax::mojom::StringListAttribute attribute,
                               std::vector<std::string>* value) const;
 
-  bool GetHtmlAttribute(const char* attr, base::string16* value) const;
-  bool GetHtmlAttribute(const char* attr, std::string* value) const;
+  bool GetHtmlAttribute(const char* attribute, base::string16* value) const;
+  bool GetHtmlAttribute(const char* attribute, std::string* value) const;
 
   // Setting accessibility attributes.
   void AddStringAttribute(ax::mojom::StringAttribute attribute,
@@ -223,7 +224,7 @@
 
   // The id of an ancestor node in the same AXTree that this object's
   // bounding box is relative to, or -1 if there's no offset container.
-  int offset_container_id = -1;
+  int32_t offset_container_id = -1;
 
   // The relative bounding box of this node.
   gfx::RectF location;
diff --git a/ui/accessibility/ax_relative_bounds.cc b/ui/accessibility/ax_relative_bounds.cc
index 322a068..e41cb3d6 100644
--- a/ui/accessibility/ax_relative_bounds.cc
+++ b/ui/accessibility/ax_relative_bounds.cc
@@ -66,4 +66,8 @@
   return result;
 }
 
+std::ostream& operator<<(std::ostream& stream, const AXRelativeBounds& bounds) {
+  return stream << bounds.ToString();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_relative_bounds.h b/ui/accessibility/ax_relative_bounds.h
index 478275a..bb43598 100644
--- a/ui/accessibility/ax_relative_bounds.h
+++ b/ui/accessibility/ax_relative_bounds.h
@@ -5,7 +5,10 @@
 #ifndef UI_ACCESSIBILITY_AX_RELATIVE_BOUNDS_H_
 #define UI_ACCESSIBILITY_AX_RELATIVE_BOUNDS_H_
 
+#include <stdint.h>
+
 #include <memory>
+#include <ostream>
 
 #include "ui/accessibility/ax_export.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -30,7 +33,7 @@
 // Otherwise, for a node other than the root, the bounds are relative to
 // the root of the tree, and for the root of a tree, the bounds are relative
 // to its immediate containing node.
-struct AX_EXPORT AXRelativeBounds {
+struct AX_EXPORT AXRelativeBounds final {
   AXRelativeBounds();
   virtual ~AXRelativeBounds();
 
@@ -43,7 +46,7 @@
 
   // The id of an ancestor node in the same AXTree that this object's
   // bounding box is relative to, or -1 if there's no offset container.
-  int offset_container_id;
+  int32_t offset_container_id;
 
   // The relative bounding box of this node.
   gfx::RectF bounds;
@@ -56,6 +59,9 @@
   std::unique_ptr<gfx::Transform> transform;
 };
 
+AX_EXPORT std::ostream& operator<<(std::ostream& stream,
+                                   const AXRelativeBounds& bounds);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_NODE_DATA_H_
diff --git a/ui/accessibility/mojom/BUILD.gn b/ui/accessibility/mojom/BUILD.gn
index 289e5ca..33ab51f 100644
--- a/ui/accessibility/mojom/BUILD.gn
+++ b/ui/accessibility/mojom/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("mojom") {
   sources = [
+    "ax_assistant_structure.mojom",
     "ax_node_data.mojom",
     "ax_tree_data.mojom",
     "ax_tree_update.mojom",
@@ -16,5 +17,6 @@
     "//ui/accessibility:ax_enums_mojo",
     "//ui/gfx/geometry/mojo",
     "//ui/gfx/mojo",
+    "//ui/gfx/range/mojo",
   ]
 }
diff --git a/ui/accessibility/mojom/ax_assistant_structure.mojom b/ui/accessibility/mojom/ax_assistant_structure.mojom
new file mode 100644
index 0000000..94b2165
--- /dev/null
+++ b/ui/accessibility/mojom/ax_assistant_structure.mojom
@@ -0,0 +1,50 @@
+// 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 ax.mojom;
+
+import "mojo/public/mojom/base/string16.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+import "ui/gfx/range/mojo/range.mojom";
+
+// Tree structure for assistant. The tree is represented as a flat array of
+// nodes, each containing a children_indices vector that points to its child
+// nodes. The purpose is to work around max depth restriction of recursive
+// data structure in mojo.
+struct AssistantTree {
+  array<AssistantNode> nodes;
+};
+
+// Represents view structure to be passed to assistant. The view structure is
+// synthesized from the AXNode.
+struct AssistantNode {
+  array<int32> children_indices;
+
+  // Geometry of the view in pixels
+  gfx.mojom.Rect rect;
+
+  // Text of the view.
+  mojo_base.mojom.String16 text;
+
+  // Text properties
+  float text_size;
+  uint32 color;
+  uint32 bgcolor;
+  bool bold;
+  bool italic;
+  bool underline;
+  bool line_through;
+
+  // Selected portion of the text.
+  gfx.mojom.Range? selection;
+
+  // Fake Android view class name of the element.  Each node is assigned
+  // a closest approximation of Android's views to keep the server happy.
+  string class_name;
+
+  // Accessibility functionality of the node inferred from DOM or based on HTML
+  // role attribute.
+  string? role;
+};
+
diff --git a/ui/accessibility/mojom/ax_assistant_structure.typemap b/ui/accessibility/mojom/ax_assistant_structure.typemap
new file mode 100644
index 0000000..fe2b8aa
--- /dev/null
+++ b/ui/accessibility/mojom/ax_assistant_structure.typemap
@@ -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.
+
+mojom = "//ui/accessibility/mojom/ax_assistant_structure.mojom"
+public_headers = [ "//ui/accessibility/ax_assistant_structure.h" ]
+traits_headers =
+    [ "//ui/accessibility/mojom/ax_assistant_structure_mojom_traits.h" ]
+sources = [
+  "ax_assistant_structure_mojom_traits.cc",
+  "ax_assistant_structure_mojom_traits.h",
+]
+public_deps = [
+  "//ui/accessibility:ax_assistant",
+  "//ui/gfx",
+  "//ui/gfx/geometry/mojo",
+  "//ui/gfx/geometry/mojo:struct_traits",
+  "//ui/gfx/range/mojo",
+  "//ui/gfx/range/mojo:struct_traits",
+]
+type_mappings = [
+  "ax.mojom.AssistantTree=ui::AssistantTree[move_only]",
+  "ax.mojom.AssistantNode=std::unique_ptr<ui::AssistantNode>[move_only]",
+]
diff --git a/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.cc b/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.cc
new file mode 100644
index 0000000..54a2014
--- /dev/null
+++ b/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.cc
@@ -0,0 +1,56 @@
+// 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 "ui/accessibility/mojom/ax_assistant_structure_mojom_traits.h"
+
+#include "mojo/public/cpp/base/string16_mojom_traits.h"
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
+#include "ui/gfx/range/mojo/range_struct_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<ax::mojom::AssistantTreeDataView, ui::AssistantTree>::Read(
+    ax::mojom::AssistantTreeDataView data,
+    ui::AssistantTree* out) {
+  if (!data.ReadNodes(&out->nodes))
+    return false;
+  for (size_t i = 0; i < out->nodes.size(); i++) {
+    // Each child's index should be greater than its parent and within the array
+    // bounds. This implies that there is no circle in the tree.
+    for (size_t child_index : out->nodes[i]->children_indices) {
+      if (child_index <= i || child_index >= out->nodes.size())
+        return false;
+    }
+  }
+  return true;
+}
+
+// static
+bool StructTraits<ax::mojom::AssistantNodeDataView,
+                  std::unique_ptr<ui::AssistantNode>>::
+    Read(ax::mojom::AssistantNodeDataView data,
+         std::unique_ptr<ui::AssistantNode>* out) {
+  DCHECK(!out->get());
+  *out = std::make_unique<ui::AssistantNode>();
+  (*out)->bgcolor = data.bgcolor();
+  (*out)->bold = data.bold();
+
+  (*out)->color = data.color();
+  (*out)->italic = data.italic();
+  (*out)->line_through = data.line_through();
+  (*out)->underline = data.underline();
+
+  if (!data.ReadRect(&(*out)->rect) || !data.ReadText(&(*out)->text) ||
+      !data.ReadRole(&(*out)->role) ||
+      !data.ReadSelection(&(*out)->selection) ||
+      !data.ReadChildrenIndices(&(*out)->children_indices) ||
+      !data.ReadClassName(&(*out)->class_name)) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.h b/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.h
new file mode 100644
index 0000000..dbbac8ec
--- /dev/null
+++ b/ui/accessibility/mojom/ax_assistant_structure_mojom_traits.h
@@ -0,0 +1,74 @@
+// 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 UI_ACCESSIBILITY_MOJOM_AX_ASSISTANT_STRUCTURE_MOJOM_TRAITS_H_
+#define UI_ACCESSIBILITY_MOJOM_AX_ASSISTANT_STRUCTURE_MOJOM_TRAITS_H_
+
+#include <memory>
+
+#include "ui/accessibility/ax_assistant_structure.h"
+#include "ui/accessibility/mojom/ax_assistant_structure.mojom-shared.h"
+#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<ax::mojom::AssistantTreeDataView, ui::AssistantTree> {
+  static bool Read(ax::mojom::AssistantTreeDataView data,
+                   ui::AssistantTree* out);
+};
+
+template <>
+struct StructTraits<ax::mojom::AssistantNodeDataView,
+                    std::unique_ptr<ui::AssistantNode>> {
+  static std::vector<int32_t> children_indices(
+      const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->children_indices;
+  }
+  static gfx::Rect rect(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->rect;
+  }
+  static base::string16 text(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->text;
+  }
+  static float text_size(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->text_size;
+  }
+  static uint32_t color(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->color;
+  }
+  static uint32_t bgcolor(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->bgcolor;
+  }
+  static bool bold(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->bold;
+  }
+  static bool italic(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->italic;
+  }
+  static bool underline(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->underline;
+  }
+  static bool line_through(const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->line_through;
+  }
+  static base::Optional<gfx::Range> selection(
+      const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->selection;
+  }
+  static std::string class_name(
+      const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->class_name;
+  }
+  static base::Optional<std::string> role(
+      const std::unique_ptr<ui::AssistantNode>& node) {
+    return node->role;
+  }
+  static bool Read(ax::mojom::AssistantNodeDataView data,
+                   std::unique_ptr<ui::AssistantNode>* out);
+};
+
+}  // namespace mojo
+
+#endif  // UI_ACCESSIBILITY_MOJOM_AX_ASSISTANT_STRUCTURE_MOJOM_TRAITS_H_
diff --git a/ui/accessibility/mojom/typemaps.gni b/ui/accessibility/mojom/typemaps.gni
index 019293d4..cd4fbb9d4 100644
--- a/ui/accessibility/mojom/typemaps.gni
+++ b/ui/accessibility/mojom/typemaps.gni
@@ -6,4 +6,5 @@
   "//ui/accessibility/mojom/ax_node_data.typemap",
   "//ui/accessibility/mojom/ax_tree_data.typemap",
   "//ui/accessibility/mojom/ax_tree_update.typemap",
+  "//ui/accessibility/mojom/ax_assistant_structure.typemap",
 ]
diff --git a/ui/accessibility/platform/ax_snapshot_node_android_platform.h b/ui/accessibility/platform/ax_snapshot_node_android_platform.h
deleted file mode 100644
index f550221..0000000
--- a/ui/accessibility/platform/ax_snapshot_node_android_platform.h
+++ /dev/null
@@ -1,83 +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 UI_ACCESSIBILITY_PLATFORM_AX_SNAPSHOT_ANDROID_H_
-#define UI_ACCESSIBILITY_PLATFORM_AX_SNAPSHOT_ANDROID_H_
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "base/optional.h"
-#include "base/strings/string16.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_export.h"
-#include "ui/accessibility/ax_tree_update.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace ui {
-
-class AXNode;
-class AXTree;
-
-// An intermediate representation of the accessibility snapshot
-// in order to share code between ARC and Android. The field names
-// are kept consistent with AccessibilitySnapshotNode.java.
-struct AXSnapshotNodeAndroid {
-  // Builds a whole tree of AXSnapshotNodeAndroid objects from
-  // an AXTreeUpdate.
-  AX_EXPORT static std::unique_ptr<AXSnapshotNodeAndroid> Create(
-      const AXTreeUpdate& update,
-      bool show_password);
-
-  // Returns a fake Android view class that is a closest
-  // approximation of the ax::mojom::Role.
-  AX_EXPORT static const char* AXRoleToAndroidClassName(ax::mojom::Role role,
-                                                        bool has_parent);
-
-  AX_EXPORT static bool AXRoleIsLink(ax::mojom::Role role);
-
-  AX_EXPORT static base::string16 AXUrlBaseText(base::string16 url);
-
-  AX_EXPORT ~AXSnapshotNodeAndroid();
-
-  gfx::Rect rect;
-  base::string16 text;
-  float text_size;
-  int32_t color;
-  int32_t bgcolor;
-  bool bold;
-  bool italic;
-  bool underline;
-  bool line_through;
-
-  bool has_selection;
-  int32_t start_selection;
-  int32_t end_selection;
-
-  base::Optional<std::string> role;
-  std::string class_name;
-  std::vector<std::unique_ptr<AXSnapshotNodeAndroid>> children;
-
- private:
-  AXSnapshotNodeAndroid();
-
-  struct WalkAXTreeConfig {
-    bool should_select_leaf;
-    const bool show_password;
-  };
-
-  static std::unique_ptr<AXSnapshotNodeAndroid> WalkAXTreeDepthFirst(
-      const AXNode* node,
-      gfx::Rect rect,
-      const AXTreeUpdate& update,
-      const AXTree* tree,
-      WalkAXTreeConfig& config);
-
-  DISALLOW_COPY_AND_ASSIGN(AXSnapshotNodeAndroid);
-};
-
-}  // namespace ui
-
-#endif  // UI_ACCESSIBILITY_PLATFORM_AX_SNAPSHOT_ANDROID_H_
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 1c5c7c92..ad15a65 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -4,6 +4,7 @@
 
 #include "ui/android/delegated_frame_host_android.h"
 
+#include "base/android/build_info.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "cc/layers/solid_color_layer.h"
@@ -25,6 +26,16 @@
 
 namespace {
 
+// Wait up to 5 seconds for the first frame to be produced. Having Android
+// display a placeholder for a longer period of time is preferable to drawing
+// nothing, and the first frame can take a while on low-end systems.
+static const int64_t kFirstFrameTimeoutSeconds = 5;
+
+// Wait up to 1 second for a frame of the correct size to be produced. Android
+// OS will only wait 4 seconds, so we limit this to 1 second to make sure we
+// have always produced a frame before the OS stops waiting.
+static const int64_t kResizeTimeoutSeconds = 1;
+
 constexpr viz::LocalSurfaceId kInvalidLocalSurfaceId;
 
 scoped_refptr<cc::SurfaceLayer> CreateSurfaceLayer(
@@ -99,7 +110,12 @@
     support_->SubmitCompositorFrame(local_surface_id, std::move(frame),
                                     std::move(hit_test_region_list));
   }
+
   compositor_attach_until_frame_lock_.reset();
+
+  DCHECK(content_layer_);
+  if (content_layer_->bounds() == expected_pixel_size_)
+    compositor_pending_resize_lock_.reset();
 }
 
 void DelegatedFrameHostAndroid::DidNotProduceFrame(
@@ -196,8 +212,8 @@
   // frame from being produced. If we already have delegated content, no need
   // to take the lock.
   if (compositor->IsDrawingFirstVisibleFrame() && !HasDelegatedContent()) {
-    compositor_attach_until_frame_lock_ =
-        compositor->GetCompositorLock(this, base::TimeDelta::FromSeconds(5));
+    compositor_attach_until_frame_lock_ = compositor->GetCompositorLock(
+        this, base::TimeDelta::FromSeconds(kFirstFrameTimeoutSeconds));
   }
   compositor->AddChildFrameSink(frame_sink_id_);
   client_->SetBeginFrameSource(&begin_frame_source_);
@@ -208,6 +224,7 @@
   if (!registered_parent_compositor_)
     return;
   compositor_attach_until_frame_lock_.reset();
+  compositor_pending_resize_lock_.reset();
   client_->SetBeginFrameSource(nullptr);
   support_->SetNeedsBeginFrame(false);
   registered_parent_compositor_->RemoveChildFrameSink(frame_sink_id_);
@@ -221,6 +238,26 @@
   // delegated_frame_host.cc. https://crbug.com/801350
 }
 
+void DelegatedFrameHostAndroid::PixelSizeWillChange(
+    const gfx::Size& pixel_size) {
+  // We never take the resize lock unless we're on O+, as previous versions of
+  // Android won't wait for us to produce the correct sized frame and will end
+  // up looking worse.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return;
+  }
+
+  expected_pixel_size_ = pixel_size;
+  if (registered_parent_compositor_) {
+    if (!content_layer_ || content_layer_->bounds() != expected_pixel_size_) {
+      compositor_pending_resize_lock_ =
+          registered_parent_compositor_->GetCompositorLock(
+              this, base::TimeDelta::FromSeconds(kResizeTimeoutSeconds));
+    }
+  }
+}
+
 void DelegatedFrameHostAndroid::DidReceiveCompositorFrameAck(
     const std::vector<viz::ReturnedResource>& resources) {
   client_->ReclaimResources(resources);
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 63277a6..97945c5 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -89,6 +89,10 @@
 
   void SynchronizeVisualProperties();
 
+  // Called when we begin a resize operation. Takes the compositor lock until we
+  // receive a frame of the expected size.
+  void PixelSizeWillChange(const gfx::Size& pixel_size);
+
   // Returns the ID for the current Surface. Returns an invalid ID if no
   // surface exists (!HasDelegatedContent()).
   const viz::SurfaceId& SurfaceId() const;
@@ -143,11 +147,19 @@
   const bool enable_surface_synchronization_;
   viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_;
 
+  // The size we are resizing to. Once we receive a frame of this size we can
+  // release any resize compositor lock.
+  gfx::Size expected_pixel_size_;
+
   // A lock that is held from the point at which we attach to the compositor to
   // the point at which we submit our first frame to the compositor. This
   // ensures that the compositor doesn't swap without a frame available.
   std::unique_ptr<ui::CompositorLock> compositor_attach_until_frame_lock_;
 
+  // A lock that is held from the point we begin resizing this frame to the
+  // point at which we receive a frame of the correct size.
+  std::unique_ptr<ui::CompositorLock> compositor_pending_resize_lock_;
+
   DISALLOW_COPY_AND_ASSIGN(DelegatedFrameHostAndroid);
 };
 
diff --git a/ui/android/delegated_frame_host_android_unittest.cc b/ui/android/delegated_frame_host_android_unittest.cc
index fc0c6c4b..288f21f 100644
--- a/ui/android/delegated_frame_host_android_unittest.cc
+++ b/ui/android/delegated_frame_host_android_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ui/android/delegated_frame_host_android.h"
+#include "base/android/build_info.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/solid_color_layer.h"
@@ -90,10 +91,10 @@
     return lock_manager_.GetCompositorLock(client, time_delta).release();
   }
 
-  void SubmitCompositorFrame() {
+  void SubmitCompositorFrame(const gfx::Size& frame_size = gfx::Size(10, 10)) {
     viz::CompositorFrame frame;
     auto render_pass = viz::RenderPass::Create();
-    render_pass->output_rect = gfx::Rect(10, 10);
+    render_pass->output_rect = gfx::Rect(frame_size);
     frame.render_pass_list.push_back(std::move(render_pass));
     frame.metadata.begin_frame_ack.sequence_number = 1;
     frame.metadata.device_scale_factor = 1;
@@ -101,6 +102,20 @@
                                        viz::mojom::HitTestRegionList::New());
   }
 
+  void SetUpValidFrame(const gfx::Size& frame_size) {
+    EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame())
+        .WillOnce(Return(true));
+    EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
+        .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
+    EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+        .Times(1);
+    frame_host_->AttachToCompositor(&compositor_);
+
+    EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false))
+        .Times(1);
+    SubmitCompositorFrame(frame_size);
+  }
+
  protected:
   MockWindowAndroidCompositor compositor_;
   ui::ViewAndroid view_;
@@ -163,5 +178,97 @@
   frame_host_->DetachFromCompositor();
 }
 
+TEST_F(DelegatedFrameHostAndroidTest, ResizeLockBasic) {
+  // Resize lock is only enabled on O+.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return;
+  }
+
+  SetUpValidFrame(gfx::Size(10, 10));
+
+  // Tell the frame host to resize, it should take a lock.
+  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
+      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+      .Times(1);
+  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
+
+  // Submit a frame of the wrong size, nothing should change.
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false))
+      .Times(0);
+  SubmitCompositorFrame(gfx::Size(20, 20));
+
+  // Submit a frame with the right size, the lock should release.
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false))
+      .Times(1);
+  SubmitCompositorFrame(gfx::Size(50, 50));
+}
+
+TEST_F(DelegatedFrameHostAndroidTest, ResizeLockNotTakenIfNoSizeChange) {
+  // Resize lock is only enabled on O+.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return;
+  }
+
+  SetUpValidFrame(gfx::Size(10, 10));
+
+  // Tell the frame host to resize to the existing size, nothing should happen.
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+      .Times(0);
+  frame_host_->PixelSizeWillChange(gfx::Size(10, 10));
+}
+
+TEST_F(DelegatedFrameHostAndroidTest, ResizeLockReleasedWithDetach) {
+  // Resize lock is only enabled on O+.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return;
+  }
+
+  SetUpValidFrame(gfx::Size(10, 10));
+
+  // Tell the frame host to resize, it should take a lock.
+  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
+      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+      .Times(1);
+  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
+
+  // Lock should be released when we detach.
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false))
+      .Times(1);
+  frame_host_->DetachFromCompositor();
+}
+
+TEST_F(DelegatedFrameHostAndroidTest, TestBothCompositorLocks) {
+  // Resize lock is only enabled on O+.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return;
+  }
+
+  // Attach during the first frame, first lock will be taken.
+  EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true));
+  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
+      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+      .Times(1);
+  frame_host_->AttachToCompositor(&compositor_);
+
+  // Tell the frame host to resize, it should take a second lock.
+  EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _))
+      .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock));
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true))
+      .Times(0);
+  frame_host_->PixelSizeWillChange(gfx::Size(50, 50));
+
+  // Submit a compositor frame of the right size, both locks should release.
+  EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false))
+      .Times(1);
+  SubmitCompositorFrame(gfx::Size(50, 50));
+}
+
 }  // namespace
 }  // namespace ui
diff --git a/ui/arc/notification/arc_notification_content_view_unittest.cc b/ui/arc/notification/arc_notification_content_view_unittest.cc
index 2a2fb6f..ee3bf45 100644
--- a/ui/arc/notification/arc_notification_content_view_unittest.cc
+++ b/ui/arc/notification/arc_notification_content_view_unittest.cc
@@ -10,9 +10,9 @@
 
 #include "ash/message_center/message_center_view.h"
 #include "ash/shell.h"
+#include "ash/system/message_center/notification_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
-#include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -320,7 +320,7 @@
   // Show MessageCenterView and activate its widget.
   auto* notification_tray =
       ash::StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-          ->web_notification_tray();
+          ->notification_tray();
   notification_tray->ShowBubble(false /* show_by_click */);
   notification_tray->GetBubbleView()
       ->GetWidget()
diff --git a/ui/aura/test/aura_test_suite_setup.cc b/ui/aura/test/aura_test_suite_setup.cc
index 7965032..35b2307 100644
--- a/ui/aura/test/aura_test_suite_setup.cc
+++ b/ui/aura/test/aura_test_suite_setup.cc
@@ -41,7 +41,7 @@
   DCHECK(!Env::GetInstanceDontCreate());
 #if BUILDFLAG(ENABLE_MUS)
   const Env::Mode env_mode =
-      features::IsMusEnabled() ? Env::Mode::MUS : Env::Mode::LOCAL;
+      features::IsMashEnabled() ? Env::Mode::MUS : Env::Mode::LOCAL;
   env_ = Env::CreateInstance(env_mode);
   if (env_mode == Env::Mode::MUS)
     ConfigureMus();
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 64d19b6..a162bb8 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -359,6 +359,7 @@
       "dragdrop/cocoa_dnd_util.mm",
       "dragdrop/file_info.cc",
       "dragdrop/file_info.h",
+      "emoji/emoji_panel_helper.h",
       "idle/idle.cc",
       "idle/idle.h",
       "idle/idle_chromeos.cc",
@@ -375,6 +376,15 @@
   }
 
   if (is_mac) {
+    sources += [ "emoji/emoji_panel_helper_mac.mm" ]
+  } else if (is_win) {
+    sources += [ "emoji/emoji_panel_helper_win.cc" ]
+  } else {
+    # Empty implementation for all other platforms.
+    sources += [ "emoji/emoji_panel_helper.cc" ]
+  }
+
+  if (is_mac) {
     sources += [ "accelerators/media_keys_listener_mac.mm" ]
   } else {
     sources += [ "accelerators/media_keys_listener_stub.cc" ]
diff --git a/ui/base/emoji/emoji_panel_helper.cc b/ui/base/emoji/emoji_panel_helper.cc
new file mode 100644
index 0000000..03a82065
--- /dev/null
+++ b/ui/base/emoji/emoji_panel_helper.cc
@@ -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.
+
+#include "ui/base/emoji/emoji_panel_helper.h"
+
+namespace ui {
+
+bool IsEmojiPanelSupported() {
+  return false;
+}
+
+void ShowEmojiPanel() {}
+
+}  // namespace ui
diff --git a/ui/base/emoji/emoji_panel_helper.h b/ui/base/emoji/emoji_panel_helper.h
new file mode 100644
index 0000000..0fbb9a80
--- /dev/null
+++ b/ui/base/emoji/emoji_panel_helper.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 UI_BASE_EMOJI_EMOJI_PANEL_HELPER_H_
+#define UI_BASE_EMOJI_EMOJI_PANEL_HELPER_H_
+
+#include "ui/base/ui_base_export.h"
+
+namespace ui {
+
+// Returns whether showing the Emoji Panel is supported on this version of
+// the operating system.
+UI_BASE_EXPORT bool IsEmojiPanelSupported();
+
+// Invokes the commands to show the Emoji Panel.
+UI_BASE_EXPORT void ShowEmojiPanel();
+
+}  // namespace ui
+
+#endif  // UI_BASE_EMOJI_EMOJI_PANEL_HELPER_H_
\ No newline at end of file
diff --git a/ui/base/emoji/emoji_panel_helper_mac.mm b/ui/base/emoji/emoji_panel_helper_mac.mm
new file mode 100644
index 0000000..d322ea5
--- /dev/null
+++ b/ui/base/emoji/emoji_panel_helper_mac.mm
@@ -0,0 +1,22 @@
+// 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 "ui/base/emoji/emoji_panel_helper.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/feature_list.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ui {
+
+bool IsEmojiPanelSupported() {
+  return base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu);
+}
+
+void ShowEmojiPanel() {
+  [NSApp orderFrontCharacterPalette:nil];
+}
+
+}  // namespace ui
diff --git a/ui/base/emoji/emoji_panel_helper_win.cc b/ui/base/emoji/emoji_panel_helper_win.cc
new file mode 100644
index 0000000..23124db1
--- /dev/null
+++ b/ui/base/emoji/emoji_panel_helper_win.cc
@@ -0,0 +1,44 @@
+// 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 "ui/base/emoji/emoji_panel_helper.h"
+
+#include <windows.h>
+
+#include "base/feature_list.h"
+#include "base/win/windows_version.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/events/keycodes/keyboard_code_conversion_win.h"
+
+namespace ui {
+
+bool IsEmojiPanelSupported() {
+  return base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu) &&
+         // Emoji picker is supported on Windows 10's Spring 2018 Update and
+         // above.
+         base::win::GetVersion() >= base::win::Version::VERSION_WIN10_RS4;
+}
+
+void ShowEmojiPanel() {
+  // This sends Windows Key + '.' (both keydown and keyup events).
+  // "SendInput" is used because Windows needs to receive these events and
+  // open the Emoji picker.
+  // TODO(crbug.com/827404): Move to a specialized Windows API once it is
+  // available.
+  INPUT input[4] = {};
+  input[0].type = INPUT_KEYBOARD;
+  input[0].ki.wVk = ui::WindowsKeyCodeForKeyboardCode(ui::VKEY_COMMAND);
+  input[1].type = INPUT_KEYBOARD;
+  input[1].ki.wVk = ui::WindowsKeyCodeForKeyboardCode(ui::VKEY_OEM_PERIOD);
+
+  input[2].type = INPUT_KEYBOARD;
+  input[2].ki.wVk = ui::WindowsKeyCodeForKeyboardCode(ui::VKEY_COMMAND);
+  input[2].ki.dwFlags |= KEYEVENTF_KEYUP;
+  input[3].type = INPUT_KEYBOARD;
+  input[3].ki.wVk = ui::WindowsKeyCodeForKeyboardCode(ui::VKEY_OEM_PERIOD);
+  input[3].ki.dwFlags |= KEYEVENTF_KEYUP;
+  ::SendInput(4, input, sizeof(INPUT));
+}
+
+}  // namespace ui
diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc
index 2bed400..91ed8f1b 100644
--- a/ui/base/models/menu_model.cc
+++ b/ui/base/models/menu_model.cc
@@ -17,7 +17,14 @@
   const int item_count = (*model)->GetItemCount();
   for (int i = 0; i < item_count; ++i) {
     const int candidate_index = i;
-    if ((*model)->GetTypeAt(candidate_index) == TYPE_SUBMENU) {
+    // Actionable submenus have commands.
+    if ((*model)->GetTypeAt(candidate_index) == TYPE_ACTIONABLE_SUBMENU &&
+        (*model)->GetCommandIdAt(candidate_index) == command_id) {
+      *index = candidate_index;
+      return true;
+    }
+    if ((*model)->GetTypeAt(candidate_index) == TYPE_SUBMENU ||
+        (*model)->GetTypeAt(candidate_index) == TYPE_ACTIONABLE_SUBMENU) {
       MenuModel* submenu_model = (*model)->GetSubmenuModelAt(candidate_index);
       if (GetModelAndIndexForCommandId(command_id, &submenu_model, index)) {
         *model = submenu_model;
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index 605dc1b..a19e6e9 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -28,12 +28,13 @@
  public:
   // The type of item.
   enum ItemType {
-    TYPE_COMMAND,
-    TYPE_CHECK,
-    TYPE_RADIO,
-    TYPE_SEPARATOR,
-    TYPE_BUTTON_ITEM,
-    TYPE_SUBMENU
+    TYPE_COMMAND,      // Performs an action when selected.
+    TYPE_CHECK,        // Can be selected/checked to toggle a boolean state.
+    TYPE_RADIO,        // Can be selected/checked among a group of choices.
+    TYPE_SEPARATOR,    // Shows a horizontal line separator.
+    TYPE_BUTTON_ITEM,  // Shows a row of buttons.
+    TYPE_SUBMENU,      // Presents a submenu within another menu.
+    TYPE_ACTIONABLE_SUBMENU,  // A SUBMENU that is also a COMMAND.
   };
 
   virtual ~MenuModel() {}
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index b7b3429..1b8eefc 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -159,6 +159,26 @@
   AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model);
 }
 
+void SimpleMenuModel::AddActionableSubMenu(int command_id,
+                                           const base::string16& label,
+                                           MenuModel* model) {
+  Item item(command_id, TYPE_ACTIONABLE_SUBMENU, label);
+  item.submenu = model;
+  AppendItem(std::move(item));
+}
+
+void SimpleMenuModel::AddActionableSubmenuWithStringIdAndIcon(
+    int command_id,
+    int string_id,
+    MenuModel* model,
+    const gfx::ImageSkia& icon) {
+  Item item(command_id, TYPE_ACTIONABLE_SUBMENU,
+            l10n_util::GetStringUTF16(string_id));
+  item.submenu = model;
+  item.icon = gfx::Image(icon);
+  AppendItem(std::move(item));
+}
+
 void SimpleMenuModel::InsertItemAt(int index,
                                    int command_id,
                                    const base::string16& label) {
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index ecc620a..feb3d66 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -93,13 +93,20 @@
   //   or SPACING. NORMAL separators are silently ignored if the model is empty.
   void AddSeparator(MenuSeparatorType separator_type);
 
-  // These three methods take pointers to various sub-models. These models
-  // should be owned by the same owner of this SimpleMenuModel.
+  // These methods take pointers to various sub-models. These models should be
+  // owned by the same owner of this SimpleMenuModel.
   void AddButtonItem(int command_id, ButtonMenuItemModel* model);
   void AddSubMenu(int command_id,
                   const base::string16& label,
                   MenuModel* model);
   void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model);
+  void AddActionableSubMenu(int command_id,
+                            const base::string16& label,
+                            MenuModel* model);
+  void AddActionableSubmenuWithStringIdAndIcon(int command_id,
+                                               int string_id,
+                                               MenuModel* model,
+                                               const gfx::ImageSkia& icon);
 
   // Methods for inserting items into the model.
   void InsertItemAt(int index, int command_id, const base::string16& label);
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 23e3147..227c00b 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -87,21 +87,12 @@
     "PrecisionTouchpadScrollPhase", base::FEATURE_ENABLED_BY_DEFAULT};
 #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.
+// Used to have ash (Chrome OS system UI) run in its own process.
+// TODO(jamescook): Make flag only available in Chrome OS.
 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
+bool IsMashEnabled() {
+  return base::FeatureList::IsEnabled(features::kMash);
 }
 
 #if defined(OS_MACOSX)
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index cc7a15d..85e6084b 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -37,15 +37,9 @@
 
 // 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();
+// Returns true if mash (out-of-process ash system UI) is enabled.
+UI_BASE_EXPORT bool IsMashEnabled();
 
 #if defined(OS_MACOSX)
 // Returns true if the NSWindows for apps will be created in the app's process,
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index 25f9980..2691c6f 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -17,8 +17,7 @@
   # 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 the features kMus
-  # (or kMash in chrome code) to start in mus/mash.
+  # Set to true to if mus (aka the window service) is enabled.
   enable_mus = is_chromeos
 }
 
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 455c6739..5ca9d25 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -279,7 +279,7 @@
 
 void Compositor::SetLocalSurfaceId(
     const viz::LocalSurfaceId& local_surface_id) {
-  host_->SetLocalSurfaceId(local_surface_id);
+  host_->SetLocalSurfaceIdFromParent(local_surface_id);
 }
 
 void Compositor::SetLayerTreeFrameSink(
@@ -367,7 +367,7 @@
 
   if (size_ != size_in_pixel && local_surface_id.is_valid()) {
     // A new LocalSurfaceId must be set when the compositor size changes.
-    DCHECK_NE(local_surface_id, host_->local_surface_id());
+    DCHECK_NE(local_surface_id, host_->local_surface_id_from_parent());
   }
 
   if (!size_in_pixel.IsEmpty()) {
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index f1cccf22..fc8f445 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1614,20 +1614,21 @@
   ManagedDisplayMode display_mode;
   if (!GetActiveModeForDisplayId(display_id, &display_mode))
     return false;
-  const std::vector<double> zooms = GetDisplayZoomFactors(display_mode);
   auto iter = display_info_.find(display_id);
   if (iter == display_info_.end())
     return false;
 
-  const double current_display_zoom = iter->second.zoom_factor();
+  const float current_display_zoom = iter->second.zoom_factor();
 
   // Find the index of |current_display_zoom| in |zooms|. The nearest value is
   // used if the exact match is not found.
+  const std::vector<float> zooms = GetDisplayZoomFactors(display_mode);
   std::size_t zoom_idx = 0;
-  double min_diff = std::abs(zooms[zoom_idx] - current_display_zoom);
+  float min_diff = std::abs(zooms[zoom_idx] - current_display_zoom);
   for (std::size_t i = 1; i < zooms.size(); i++) {
-    if (std::abs(current_display_zoom - zooms[i]) < min_diff) {
-      min_diff = std::abs(current_display_zoom - zooms[i]);
+    const float diff = std::abs(current_display_zoom - zooms[i]);
+    if (diff < min_diff) {
+      min_diff = diff;
       zoom_idx = i;
     }
   }
@@ -1652,7 +1653,7 @@
   auto iter = display_info_.find(display_id);
   if (iter == display_info_.end())
     return;
-  if (std::abs(iter->second.zoom_factor() - 1.f) > 0.001) {
+  if (std::abs(iter->second.zoom_factor() - 1.f) > 0.001f) {
     iter->second.set_zoom_factor(1.f);
     UpdateDisplays();
   }
diff --git a/ui/display/manager/display_util.cc b/ui/display/manager/display_util.cc
index e3ff3d3..bbb6c05 100644
--- a/ui/display/manager/display_util.cc
+++ b/ui/display/manager/display_util.cc
@@ -16,11 +16,6 @@
 namespace display {
 namespace {
 
-// The list of deltas between two consecutive zoom level. Any display must have
-// one of these values as the difference between two consecutive zoom level.
-constexpr std::array<double, 7> kZoomFactorDeltas = {0.05f, 0.1f, 0.15f, 0.2f,
-                                                     0.25f, 0.5f, 1.f};
-
 // The maximum logical resolution width allowed when zooming out for a display.
 constexpr int kDefaultMaxZoomWidth = 4096;
 
@@ -30,6 +25,37 @@
 // The total number of display zoom factors to enumerate.
 constexpr int kNumOfZoomFactors = 9;
 
+bool WithinEpsilon(float a, float b) {
+  return std::abs(a - b) < std::numeric_limits<float>::epsilon();
+}
+
+// Clamps the delta between consecutive zoom factors to a user friendly and UI
+// friendly value.
+float ClampToUserFriendlyDelta(float delta) {
+  // NOTE: If these thresholds are updated, please also update aura-shell.xml.
+  // The list of deltas between two consecutive zoom level. Any display must
+  // have one of these values as the difference between two consecutive zoom
+  // level.
+  // The array of pair represents which user friendly delta value must the given
+  // raw |delta| be clamped to.
+  // std::pair::first - represents the threshold.
+  // std::pair::second - represents the user friendly clamped delta
+  constexpr std::array<std::pair<float, float>, 7> kZoomFactorDeltas = {
+      {{0.05f, 0.05f},
+       {0.1f, 0.1f},
+       {0.15f, 0.15f},
+       {0.2f, 0.2f},
+       {0.25f, 0.25f},
+       {0.7f, 0.3f},
+       {1.f, 0.5f}}};
+  std::size_t delta_index = 0;
+  while (delta_index < kZoomFactorDeltas.size() &&
+         delta >= kZoomFactorDeltas[delta_index].first) {
+    delta_index++;
+  }
+  return kZoomFactorDeltas[delta_index - 1].second;
+}
+
 }  // namespace
 
 std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
@@ -90,7 +116,7 @@
   return !(type & DISPLAY_CONNECTION_TYPE_NETWORK);
 }
 
-std::vector<double> GetDisplayZoomFactors(const ManagedDisplayMode& mode) {
+std::vector<float> GetDisplayZoomFactors(const ManagedDisplayMode& mode) {
   const int effective_width = std::round(
       static_cast<float>(mode.size().width()) / mode.device_scale_factor());
 
@@ -110,19 +136,19 @@
   // and clamping was performed, then update the total range of logical
   // resolutions and ensure that everything lies within the maximum and minimum
   // resolution range.
-  const int interval = std::round(static_cast<double>(effective_width) * 1.5f);
+  const int interval = std::round(static_cast<float>(effective_width) * 1.5f);
   if (max_effective_width == max_width)
     min_effective_width = std::max(max_effective_width - interval, min_width);
   if (min_effective_width == min_width)
     max_effective_width = std::min(min_effective_width + interval, max_width);
 
-  double max_zoom = static_cast<double>(effective_width) /
-                    static_cast<double>(min_effective_width);
-  double min_zoom = static_cast<double>(effective_width) /
-                    static_cast<double>(max_effective_width);
+  float max_zoom = static_cast<float>(effective_width) /
+                   static_cast<float>(min_effective_width);
+  float min_zoom = static_cast<float>(effective_width) /
+                   static_cast<float>(max_effective_width);
 
-  double delta =
-      (max_zoom - min_zoom) / static_cast<double>(kNumOfZoomFactors - 1);
+  float delta =
+      (max_zoom - min_zoom) / static_cast<float>(kNumOfZoomFactors - 1);
 
   // Number of zoom values above 100% zoom.
   const int zoom_in_count = std::round((max_zoom - 1.f) / delta);
@@ -130,21 +156,65 @@
   // Number of zoom values below 100% zoom.
   const int zoom_out_count = kNumOfZoomFactors - zoom_in_count - 1;
 
-  // Clamp the delta between consecutive zoom factors to a user friendly and UI
-  // friendly value.
-  std::size_t delta_index = 0;
-  while (delta_index < kZoomFactorDeltas.size() &&
-         delta >= kZoomFactorDeltas[delta_index]) {
-    delta_index++;
-  }
-  delta = kZoomFactorDeltas[delta_index - 1];
+  delta = ClampToUserFriendlyDelta(delta);
 
+  // Populate the zoom values list.
   min_zoom = 1.f - delta * zoom_out_count;
-
-  std::vector<double> zoom_values;
+  std::vector<float> zoom_values;
   for (int i = 0; i < kNumOfZoomFactors; i++)
     zoom_values.push_back(min_zoom + i * delta);
+
+  // Make sure the inverse of the internal device scale factor is included in
+  // the list. This ensures that the users have a way to go to the native
+  // resolution and 1.0 effective device scale factor.
+  InsertInverseDsfIntoList(&zoom_values, 1.f / mode.device_scale_factor());
+
+  DCHECK_EQ(zoom_values.size(), static_cast<std::size_t>(kNumOfZoomFactors));
   return zoom_values;
 }
 
+void InsertInverseDsfIntoList(std::vector<float>* zoom_values,
+                              float inverse_dsf) {
+  // We can never set the device scale factor of a display that is < 1. Hence
+  // the inverse can never be greater than 1.
+  DCHECK(inverse_dsf <= 1.f);
+
+  // 1.0 is already in the list of |zoom_values|. We do not need to add it.
+  if (WithinEpsilon(inverse_dsf, 1.f))
+    return;
+
+  auto it =
+      std::lower_bound(zoom_values->begin(), zoom_values->end(), inverse_dsf);
+
+  // We dont need to add it if the inverse dsf is already in the list.
+  if (WithinEpsilon(*it, inverse_dsf))
+    return;
+
+  if (it != zoom_values->begin()) {
+    // True if |inverse_dsf| is closer to |it| than it is to |it-1|.
+    const bool inverse_dsf_closer_to_it =
+        std::abs(*it - inverse_dsf) < std::abs(*(it - 1) - inverse_dsf);
+
+    if (WithinEpsilon(*it, 1.f) || !inverse_dsf_closer_to_it) {
+      // This cases handles the following 2 situations:
+      // - |it| points to 1.0 zoom level which we cannot replace.
+      // - If |it-1| is closer to |inverse_dsf| than |it|.
+      *(it - 1) = inverse_dsf;
+    } else {
+      // |inverse_dsf| is closer to |it| than it is to |it - 1|.
+      *it = inverse_dsf;
+    }
+  } else {
+    if (WithinEpsilon(*it, 1.f)) {
+      // If the first element in the list is 1.0 then we cannot replace it. The
+      // |inverse_dsf| needs to be inserted before it and the last item in the
+      // list needs to be removed to keep the list size fixed.
+      zoom_values->insert(zoom_values->begin(), inverse_dsf);
+      zoom_values->pop_back();
+    } else {
+      *it = inverse_dsf;
+    }
+  }
+}
+
 }  // namespace display
diff --git a/ui/display/manager/display_util.h b/ui/display/manager/display_util.h
index 3c5686e..97c7dc1 100644
--- a/ui/display/manager/display_util.h
+++ b/ui/display/manager/display_util.h
@@ -37,9 +37,17 @@
 bool IsPhysicalDisplayType(DisplayConnectionType type);
 
 // Returns a list of display zooms supported by the given |mode|.
-std::vector<double> DISPLAY_MANAGER_EXPORT
+std::vector<float> DISPLAY_MANAGER_EXPORT
 GetDisplayZoomFactors(const ManagedDisplayMode& mode);
 
+// This function adds |inverse_dsf| to the vector of |zoom_values| by replacing
+// the element it is closest to in the list. It also ensures that it never
+// replaces the default zoom value of 1.0 from the list and that the size of the
+// list never changes.
+// Exposed for testing.
+void DISPLAY_MANAGER_EXPORT
+InsertInverseDsfIntoList(std::vector<float>* zoom_values, float inverse_dsf);
+
 }  // namespace display
 
 #endif  // UI_DISPLAY_MANAGER_DISPLAY_UTIL_H_
diff --git a/ui/display/manager/display_utils_unittest.cc b/ui/display/manager/display_utils_unittest.cc
index 446e916..5a83713 100644
--- a/ui/display/manager/display_utils_unittest.cc
+++ b/ui/display/manager/display_utils_unittest.cc
@@ -18,13 +18,25 @@
 constexpr std::size_t kNumOfZoomFactors = 9;
 constexpr int kDefaultMaxZoomWidth = 4096;
 constexpr int kDefaultMinZoomWidth = 640;
+
+// Returns the index of element |val| in list |list|. Returns the size of list
+// if element is not found.
+std::size_t GetIndexOfElement(const std::vector<float>& list, float val) {
+  return std::find(list.begin(), list.end(), val) - list.begin();
+}
+
+// Returns true if |a| and |b| are equal within the error limits.
+bool WithinEpsilon(float a, float b) {
+  return std::abs(a - b) < std::numeric_limits<float>::epsilon();
+}
+
 }  // namespace
 using DisplayUtilTest = testing::Test;
 
 TEST_F(DisplayUtilTest, DisplayZooms) {
   // A vector of pairs where each pair is the resolution width correspoinding
   // to its list of available display zoom values.
-  const std::vector<std::pair<int, std::vector<double>>> expected_zoom_values{
+  const std::vector<std::pair<int, std::vector<float>>> expected_zoom_values{
       {480, {0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f}},
       {640, {0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f}},
       {720, {0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f, 1.05f}},
@@ -37,31 +49,32 @@
       {1600, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}},
       {1920, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}},
       {2160, {0.60f, 0.80f, 1.f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f}},
+      {2400, {0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f}},
       {2560, {0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f}},
       {2880, {0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f}},
-      {3200, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
-      {3840, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
-      {4096, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
-      {5120, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
-      {7680, {1.f, 2.00f, 3.00f, 4.00f, 5.00f, 6.00f, 7.00f, 8.00f, 9.00f}},
-      {8192, {1.f, 2.00f, 3.00f, 4.00f, 5.00f, 6.00f, 7.00f, 8.00f, 9.00f}},
+      {3200, {1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f, 3.00f}},
+      {3840, {1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f, 3.00f}},
+      {4096, {1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f, 3.00f}},
+      {5120, {1.f, 1.30f, 1.60f, 1.90f, 2.20f, 2.50f, 2.80f, 3.10f, 3.40f}},
+      {7680, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
+      {8192, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}},
   };
 
   for (const auto& pair : expected_zoom_values) {
     const int size = pair.first;
     ManagedDisplayMode mode(gfx::Size(size, size), 60, false, true, 1.f, 1.f);
-    const std::vector<double> zoom_values = GetDisplayZoomFactors(mode);
-    EXPECT_EQ(zoom_values.size(), kNumOfZoomFactors);
+    const std::vector<float> zoom_values = GetDisplayZoomFactors(mode);
+    EXPECT_FLOAT_EQ(zoom_values.size(), kNumOfZoomFactors);
     for (std::size_t j = 0; j < kNumOfZoomFactors; j++) {
-      EXPECT_NEAR(zoom_values[j], pair.second[j], 0.001);
+      EXPECT_FLOAT_EQ(zoom_values[j], pair.second[j]);
 
-      // Display pref stores the zoom value only upto 2 decimal places. This
-      // check ensures that the expected precision is only upto 2 decimal
-      // points. Before changing this line please ensure that you have updated
+      // Display pref stores the zoom value as double. This check ensures that
+      // the values dont lose precision.
+      // Before changing this line please ensure that you have updated
       // chromeos/display/display_prefs.cc
-      int percentage_value = std::round(zoom_values[j] * 100.f);
-      float fractional_value = static_cast<float>(percentage_value) / 100.f;
-      EXPECT_NEAR(zoom_values[j], fractional_value, 0.0001f);
+      double double_value = static_cast<double>(zoom_values[j]);
+      float float_value = static_cast<float>(double_value);
+      EXPECT_FLOAT_EQ(zoom_values[j], float_value);
     }
 
     const int effective_minimum_width_possible = size / zoom_values.back();
@@ -76,24 +89,50 @@
 }
 
 TEST_F(DisplayUtilTest, DisplayZoomsWithInternalDsf) {
-  const std::vector<int> sizes = {1280, 1366, 1440, 1600, 1920, 2160, 2560,
-                                  2880, 3200, 3840, 4096, 5120, 7680, 8192};
+  const std::vector<int> sizes = {1280, 1366, 1440, 1600, 1920,
+                                  2160, 2400, 2560, 2880, 3200,
+                                  3840, 4096, 5120, 7680, 8192};
 
   const std::vector<float> dsfs = {1.25f, 1.5f, 1.6f, 1.8f, 2.f, 2.25f};
 
   for (float dsf : dsfs) {
     for (int size : sizes) {
       ManagedDisplayMode mode(gfx::Size(size, size), 60, false, true, 1.f, dsf);
-      const std::vector<double> zoom_values = GetDisplayZoomFactors(mode);
+      const std::vector<float> zoom_values = GetDisplayZoomFactors(mode);
 
       const int effective_size = std::round(static_cast<float>(size) / dsf);
       ManagedDisplayMode expected_mode(
           gfx::Size(effective_size, effective_size), 60, false, true, 1.f, 1.f);
-      const std::vector<double> expected_zoom_values =
+      const std::vector<float> expected_zoom_values =
           GetDisplayZoomFactors(expected_mode);
       EXPECT_EQ(zoom_values.size(), kNumOfZoomFactors);
-      for (std::size_t i = 0; i < kNumOfZoomFactors; i++)
-        EXPECT_NEAR(zoom_values[i], expected_zoom_values[i], 0.001);
+
+      // There should be exactly 1 entry for the inverse dsf in the list.
+      const float inverse_dsf = 1.f / dsf;
+      const std::size_t count_of_inverse_dsf =
+          std::count_if(zoom_values.begin(), zoom_values.end(),
+                        [inverse_dsf](float zoom_value) -> bool {
+                          return WithinEpsilon(zoom_value, inverse_dsf);
+                        });
+      EXPECT_EQ(count_of_inverse_dsf, 1UL);
+
+      // Check all values in |zoom_values| are present in |expected_zoom_values|
+      // except for |inverse_dsf|.
+      auto it = expected_zoom_values.begin();
+      for (const auto& zoom_value : zoom_values) {
+        // |inverse_dsf| is not expected to be in the expected list since it was
+        // added later on.
+        if (WithinEpsilon(zoom_value, inverse_dsf))
+          continue;
+
+        // Check if |zoom_value| is present in |expected_zoom_values|.
+        it = std::find_if(it, expected_zoom_values.end(),
+                          [&zoom_value](float expected_zoom) -> bool {
+                            return WithinEpsilon(zoom_value, expected_zoom);
+                          });
+        EXPECT_TRUE(it != expected_zoom_values.end())
+            << zoom_value << " not found";
+      }
 
       const int effective_minimum_width_possible = size / zoom_values.back();
       const int effective_maximum_width_possible = size * zoom_values.front();
@@ -107,5 +146,52 @@
   }
 }
 
+TEST_F(DisplayUtilTest, InsertInverseDsfIntoList) {
+  std::vector<float> list;
+  float inverse_dsf;
+
+  list = {0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.f};
+  inverse_dsf = 0.6f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 0UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.f, 1.05f};
+  inverse_dsf = 0.6f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 0UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.1f, 1.2f, 1.3f, 1.4f};
+  inverse_dsf = 0.67f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 1UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.1f, 1.2f, 1.3f, 1.4f};
+  inverse_dsf = 0.9f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 3UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.1f, 1.2f, 1.3f, 1.4f};
+  inverse_dsf = 0.99f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 3UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {0.8f, 1.f, 1.2f, 1.4f, 1.6f, 1.8f, 2.f, 2.2f, 2.4f};
+  inverse_dsf = 0.99f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 0UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+
+  list = {1.f, 1.25f, 1.5f, 1.75f, 2.f, 2.25f, 2.5f, 2.75f, 3.f};
+  inverse_dsf = 0.85f;
+  InsertInverseDsfIntoList(&list, inverse_dsf);
+  EXPECT_EQ(GetIndexOfElement(list, inverse_dsf), 0UL);
+  EXPECT_EQ(list.size(), kNumOfZoomFactors);
+}
+
 }  // namespace test
 }  // namespace display
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 7560d1c..965ef35 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -690,6 +690,7 @@
 
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
     const WebGestureEvent& gesture_event) {
+  TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollBegin");
   if (gesture_scroll_on_impl_thread_)
     CancelCurrentFling();
 
@@ -725,9 +726,7 @@
   in_inertial_scrolling_ = false;
   switch (scroll_status.thread) {
     case cc::InputHandler::SCROLL_ON_IMPL_THREAD:
-      TRACE_EVENT_INSTANT0("input",
-                           "InputHandlerProxy::handle_input gesture scroll",
-                           TRACE_EVENT_SCOPE_THREAD);
+      TRACE_EVENT_INSTANT0("input", "Handle On Impl", TRACE_EVENT_SCOPE_THREAD);
       gesture_scroll_on_impl_thread_ = true;
       if (input_handler_->IsCurrentlyScrollingViewport())
         client_->DidStartScrollingViewport();
@@ -739,9 +738,11 @@
       break;
     case cc::InputHandler::SCROLL_UNKNOWN:
     case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
+      TRACE_EVENT_INSTANT0("input", "Handle On Main", TRACE_EVENT_SCOPE_THREAD);
       result = DID_NOT_HANDLE;
       break;
     case cc::InputHandler::SCROLL_IGNORED:
+      TRACE_EVENT_INSTANT0("input", "Ignore Scroll", TRACE_EVENT_SCOPE_THREAD);
       scroll_sequence_ignored_ = true;
       result = DROP_EVENT;
       break;
@@ -762,12 +763,14 @@
 
   gfx::Vector2dF scroll_delta(-gesture_event.data.scroll_update.delta_x,
                               -gesture_event.data.scroll_update.delta_y);
-  TRACE_EVENT_INSTANT2("input", "InputHandlerProxy::HandleGestureScrollUpdate",
-                       TRACE_EVENT_SCOPE_THREAD, "dx", scroll_delta.x(), "dy",
-                       scroll_delta.y());
+  TRACE_EVENT2("input", "InputHandlerProxy::HandleGestureScrollUpdate", "dx",
+               scroll_delta.x(), "dy", scroll_delta.y());
 
-  if (scroll_sequence_ignored_)
+  if (scroll_sequence_ignored_) {
+    TRACE_EVENT_INSTANT0("input", "Scroll Sequence Ignored",
+                         TRACE_EVENT_SCOPE_THREAD);
     return DROP_EVENT;
+  }
 
   if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
     return DID_NOT_HANDLE;
@@ -788,10 +791,14 @@
       case cc::InputHandler::SCROLL_ON_IMPL_THREAD:
         return DID_HANDLE;
       case cc::InputHandler::SCROLL_IGNORED:
+        TRACE_EVENT_INSTANT0("input", "Scroll Ignored",
+                             TRACE_EVENT_SCOPE_THREAD);
         return DROP_EVENT;
       case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
       case cc::InputHandler::SCROLL_UNKNOWN:
         if (input_handler_->ScrollingShouldSwitchtoMainThread()) {
+          TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
+                               TRACE_EVENT_SCOPE_THREAD);
           gesture_scroll_on_impl_thread_ = false;
           client_->GenerateScrollBeginAndSendToMainThread(gesture_event);
         }
@@ -832,6 +839,7 @@
 
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
   const WebGestureEvent& gesture_event) {
+  TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollEnd");
 #ifndef NDEBUG
   DCHECK(expect_scroll_update_end_);
   expect_scroll_update_end_ = false;
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index c7254cc..6119aeb 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -492,20 +492,26 @@
     event) {
   // If there is no focused window, then create a new one opened on the
   // mounted FSP volume.
-  this.findFocusedWindow_().then(function(key) {
-    if (key === null &&
-        event.eventType === 'mount' &&
-        (event.status === 'success' ||
-         event.status === 'error_path_already_mounted') &&
-        event.volumeMetadata.mountContext === 'user' &&
-        event.volumeMetadata.volumeType ===
-            VolumeManagerCommon.VolumeType.PROVIDED &&
-        event.volumeMetadata.source === VolumeManagerCommon.Source.FILE) {
-      this.navigateToVolumeWhenReady_(event.volumeMetadata.volumeId);
-    }
-  }.bind(this)).catch(function(error) {
-    console.error(error.stack || error);
-  });
+  this.findFocusedWindow_()
+      .then(function(key) {
+        let statusOK = event.status === 'success' ||
+            event.status === 'error_path_already_mounted';
+        let volumeTypeOK =
+            event.volumeMetadata.source === VolumeManagerCommon.Source.FILE ||
+            VolumeManagerCommon.getProvidedFileSystemIdFromVolumeId(
+                event.volumeId) ===
+                VolumeManagerCommon.ProvidedFileSystem.CROSTINI;
+        if (key === null && event.eventType === 'mount' && statusOK &&
+            event.volumeMetadata.mountContext === 'user' &&
+            event.volumeMetadata.volumeType ===
+                VolumeManagerCommon.VolumeType.PROVIDED &&
+            volumeTypeOK) {
+          this.navigateToVolumeWhenReady_(event.volumeMetadata.volumeId);
+        }
+      }.bind(this))
+      .catch(function(error) {
+        console.error(error.stack || error);
+      });
 };
 
 /**
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_common.js b/ui/file_manager/file_manager/common/js/volume_manager_common.js
index ad8df50..1a0c487 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_common.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_common.js
@@ -102,6 +102,9 @@
 
   // 'Add new services' menu item.
   ADD_NEW_SERVICES_MENU: 'add_new_services_menu',
+
+  // Fake root for SFTP Mount such as Linux Files.
+  SFTP_MOUNT: 'sftp_mount',
 };
 Object.freeze(VolumeManagerCommon.RootType);
 
@@ -131,6 +134,7 @@
   VolumeManagerCommon.RootType.RECENT,
   VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT,
   VolumeManagerCommon.RootType.ADD_NEW_SERVICES_MENU,
+  VolumeManagerCommon.RootType.SFTP_MOUNT,
 ];
 console.assert(
     Object.keys(VolumeManagerCommon.RootType).length ===
@@ -321,6 +325,26 @@
       volumeId.split(':', 2)[1]);
 };
 
+
+/**
+ * List of known FSP-provided fileSystemId values.
+ *
+ * @enum {string}
+ * @const
+ */
+VolumeManagerCommon.ProvidedFileSystem = {
+  CROSTINI: 'crostini',
+};
+
+/**
+ * Obtains fileSystemId from volumeId of FSP-provided mount.
+ * @param {string} volumeId Volume ID.
+ * @return {string|undefined}
+ */
+VolumeManagerCommon.getProvidedFileSystemIdFromVolumeId = function(volumeId) {
+  return volumeId ? volumeId.split(':', 3)[2] : undefined;
+};
+
 /**
  * Fake entries for virtual folders which hold Google Drive offline files,
  * Google Drive "Shared with me" files, and mixed Recent files.
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index 28ddd0d..fe568fe 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -234,7 +234,8 @@
       url(../images/volumes/2x/service_drive_active.png) 2x);
 }
 
-[volume-type-icon='team_drives_grand_root'],[volume-type-icon='team_drive'] {
+[volume-type-icon='team_drives_grand_root'],
+[volume-type-icon='team_drive'] {
   background-image: -webkit-image-set(
       url(../images/volumes/team_drive.png) 1x,
       url(../images/volumes/2x/team_drive.png) 2x);
@@ -416,3 +417,20 @@
       url(../images/volumes/recent_active.png) 1x,
       url(../images/volumes/2x/recent_active.png) 2x);
 }
+
+[sftp-mount-icon='linux-files'],
+[volume-subtype='crostini'] {
+  /* Need !important to override inline style applied to provided volumes. */
+  background-image: -webkit-image-set(
+      url(../images/volumes/linux_files.png) 1x,
+      url(../images/volumes/2x/linux_files.png) 2x) !important;
+}
+
+.tree-row[selected] [sftp-mount-icon='linux-files'],
+.tree-row[selected] [volume-subtype='crostini'] {
+  /* Need !important to override inline style applied to provided volumes. */
+  background-image: -webkit-image-set(
+      url(../images/volumes/linux_files_active.png) 1x,
+      url(../images/volumes/2x/linux_files_active.png) 2x) !important;
+}
+
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files.png b/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files.png
new file mode 100644
index 0000000..020c786
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files_active.png b/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files_active.png
new file mode 100644
index 0000000..ed90b384
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/2x/linux_files_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/linux_files.png b/ui/file_manager/file_manager/foreground/images/volumes/linux_files.png
new file mode 100644
index 0000000..c4a76e8
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/linux_files.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/volumes/linux_files_active.png b/ui/file_manager/file_manager/foreground/images/volumes/linux_files_active.png
new file mode 100644
index 0000000..cf67c13
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/volumes/linux_files_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model.js b/ui/file_manager/file_manager/foreground/js/actions_model.js
index 54e0cb3..064560e 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model.js
@@ -71,13 +71,20 @@
  * @param {!Array<!Entry>} entries
  * @param {!ActionModelUI} ui
  * @param {!VolumeManagerWrapper} volumeManager
- * @return {DriveShareAction}
+ * @return {DriveShareAction|DriveManageAction}
  */
 DriveShareAction.create = function(entries, volumeManager, ui) {
   if (entries.length !== 1)
     return null;
 
-  return new DriveShareAction(entries[0], volumeManager, ui);
+  // The share URL for Team Drives currently does not support embedding. For
+  // Team Drives entries, instead point the user to the 'Manage' action.
+  var entry = entries[0];
+  if (util.isTeamDriveEntry(entry)) {
+    return new DriveManageAction(entry, volumeManager, ui);
+  }
+
+  return new DriveShareAction(entry, volumeManager, ui);
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
index 8ef31746..a058ec0 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
@@ -304,7 +304,8 @@
         // "share" action is enabled for Team Drive directories.
         var shareAction = actions[ActionsModel.CommonActionId.SHARE];
         assertTrue(!!shareAction);
-        assertTrue(shareAction.canExecute());
+        // TODO(sashab): Re-enable when 'Manage' works for directories.
+        assertFalse(shareAction.canExecute());
 
         // "manage in drive" action is disabled for Team Drive directories.
         var manageAction =
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 0d4eb71..86629d0 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -1164,13 +1164,15 @@
     }.bind(this));
   }
 
-  // If a new file backed provided volume is mounted, then redirect to it in
-  // the focused window. Note, that this is a temporary solution for
-  // crbug.com/427776.
-  if (window.isFocused() &&
-      event.added.length === 1 &&
+  // If a new file backed provided volume, or crostini is mounted,
+  // then redirect to it in the focused window.
+  // Note, that this is a temporary solution for https://crbug.com/427776.
+  if (window.isFocused() && event.added.length === 1 &&
       event.added[0].volumeType === VolumeManagerCommon.VolumeType.PROVIDED &&
-      event.added[0].source === VolumeManagerCommon.Source.FILE) {
+      (event.added[0].source === VolumeManagerCommon.Source.FILE ||
+       VolumeManagerCommon.getProvidedFileSystemIdFromVolumeId(
+           event.added[0].volumeId) ===
+           VolumeManagerCommon.ProvidedFileSystem.CROSTINI)) {
     event.added[0].resolveDisplayRoot().then(function(displayRoot) {
       // Resolving a display root on FSP volumes is instant, despite the
       // asynchronous call.
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
index d03836b9..6ebf09f 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -10,6 +10,7 @@
   VOLUME: 'volume',
   MENU: 'menu',
   RECENT: 'recent',
+  SFTP_MOUNT: 'sftp_mount',
 };
 
 /**
@@ -131,6 +132,38 @@
 };
 
 /**
+ * Item of NavigationListModel for an SFTP Mount as used by Linux files.
+ *
+ * @param {string} label Label on the item.
+ * @param {!FakeEntry} entry Fake entry for the SFTP Mount root folder.
+ * @param {string} icon CSS icon.
+ * @constructor
+ * @extends {NavigationModelItem}
+ * @struct
+ */
+function NavigationModelSFTPMountItem(label, entry, icon) {
+  NavigationModelItem.call(this, label, NavigationModelItemType.SFTP_MOUNT);
+  this.entry_ = entry;
+  this.icon_ = icon;
+}
+
+NavigationModelSFTPMountItem.prototype = /** @struct */ {
+  __proto__: NavigationModelItem.prototype,
+  get entry() {
+    return this.entry_;
+  },
+  get icon() {
+    return this.icon_;
+  },
+  /**
+   * Start crostini container and mount it.
+   */
+  mount: function() {
+    chrome.fileManagerPrivate.mountCrostiniContainer();
+  },
+};
+
+/**
  * A navigation list model. This model combines multiple models.
  * @param {!VolumeManagerWrapper} volumeManager VolumeManagerWrapper instance.
  * @param {(!cr.ui.ArrayDataModel|!FolderShortcutsDataModel)} shortcutListModel
@@ -163,12 +196,19 @@
   this.recentModelItem_ = recentModelItem;
 
   /**
+   * Root folder for crostini Linux Files.
+   * This field will be set asynchronously after calling
+   * chrome.fileManagerPrivate.isCrostiniEnabled.
+   * @private {NavigationModelSFTPMountItem}
+   */
+  this.linuxFilesItem_ = null;
+
+  /**
    * @private {NavigationModelMenuItem}
    * @const
    */
   this.addNewServicesItem_ = addNewServicesItem;
 
-
   /**
    * All root navigation items in display order.
    * @private {!Array<!NavigationModelItem>}
@@ -210,7 +250,25 @@
     this.shortcutList_.push(entryToModelItem(shortcutEntry));
   }
 
-  // Reorder volumes, shortcuts, and optional items.
+  // Check if crostini is enabled to create linuxFilesItem_.
+  chrome.fileManagerPrivate.isCrostiniEnabled((enabled) => {
+    if (!enabled)
+      return;
+
+    this.linuxFilesItem_ = new NavigationModelSFTPMountItem(
+        str('LINUX_FILES_ROOT_LABEL'), {
+          isDirectory: true,
+          rootType: VolumeManagerCommon.RootType.SFTP_MOUNT,
+          toURL: function() {
+            return 'fake-entry://linux-files';
+          },
+        },
+        'linux-files');
+    // Reorder items to ensure Linux Files is shown.
+    this.reorderNavigationItems_();
+  });
+
+  // Reorder volumes, shortcuts, and optional items for initial display.
   this.reorderNavigationItems_();
 
   // Generates a combined 'permuted' event from an event of either volumeList or
@@ -333,14 +391,30 @@
  *  1. Volumes.
  *  2. If Downloads exists, then immediately after Downloads should be:
  *  2a. Recent if it exists.
+ *  2b. Linux Files if it exists and is not mounted.
+ *      When mounted, it will be located in Volumes at this position.
  *  3. Shortcuts.
  *  4. Add new services if it exists.
  * @private
  */
 NavigationListModel.prototype.reorderNavigationItems_ = function() {
+  // Check if Linux files already mounted.
+  let linuxFilesMounted = false;
+  for (let i = 0; i < this.volumeList_.length; i++) {
+    if (VolumeManagerCommon.getProvidedFileSystemIdFromVolumeId(
+            this.volumeList_[i].volumeInfo.volumeId) ===
+        VolumeManagerCommon.ProvidedFileSystem.CROSTINI) {
+      linuxFilesMounted = true;
+      break;
+    }
+  }
+
   // Items as per required order.
   this.navigationItems_ = this.volumeList_.slice();
   var downloadsVolumeIndex = this.findDownloadsVolumeIndex_();
+  if (this.linuxFilesItem_ && !linuxFilesMounted && downloadsVolumeIndex >= 0)
+    this.navigationItems_.splice(
+        downloadsVolumeIndex + 1, 0, this.linuxFilesItem_);
   if (this.recentModelItem_ && downloadsVolumeIndex >= 0)
     this.navigationItems_.splice(
         downloadsVolumeIndex + 1, 0, this.recentModelItem_);
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
index de5cd52..9ec752ce 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
@@ -11,7 +11,8 @@
 // Set up string assets.
 loadTimeData.data = {
   DRIVE_DIRECTORY_LABEL: 'My Drive',
-  DOWNLOADS_DIRECTORY_LABEL: 'Downloads'
+  DOWNLOADS_DIRECTORY_LABEL: 'Downloads',
+  LINUX_FILES_ROOT_LABEL: 'Linux Files',
 };
 
 function setUp() {
@@ -19,6 +20,15 @@
   // Override VolumeInfo.prototype.resolveDisplayRoot.
   VolumeInfoImpl.prototype.resolveDisplayRoot = function() {};
 
+  // Mock chrome.fileManagerPrivate.isCrostiniEnabled.
+  // TODO(crbug.com/834103): Add integration test for Crostini.
+  chrome.fileManagerPrivate = {
+    crostiniEnabled: false,
+    isCrostiniEnabled: function(callback) {
+      callback(this.crostiniEnabled);
+    },
+  };
+
   drive = new MockFileSystem('drive');
   hoge = new MockFileSystem('removable:hoge');
 }
@@ -33,22 +43,24 @@
     }
   };
   var recentItem = new NavigationModelRecentItem('recent-label', fakeEntry);
+  chrome.fileManagerPrivate.crostiniEnabled = true;
   var addNewServicesItem = new NavigationModelMenuItem(
       'menu-button-label', '#add-new-services', 'menu-button-icon');
   var model = new NavigationListModel(
       volumeManager, shortcutListModel, recentItem, addNewServicesItem);
 
-  assertEquals(5, model.length);
+  assertEquals(6, model.length);
   assertEquals('drive', model.item(0).volumeInfo.volumeId);
   assertEquals('downloads', model.item(1).volumeInfo.volumeId);
   assertEquals('fake-entry://recent', model.item(2).entry.toURL());
-  assertEquals('/root/shortcut', model.item(3).entry.fullPath);
-  assertEquals('menu-button-label', model.item(4).label);
-  assertEquals('#add-new-services', model.item(4).menu);
-  assertEquals('menu-button-icon', model.item(4).icon);
+  assertEquals('fake-entry://linux-files', model.item(3).entry.toURL());
+  assertEquals('/root/shortcut', model.item(4).entry.fullPath);
+  assertEquals('menu-button-label', model.item(5).label);
+  assertEquals('#add-new-services', model.item(5).menu);
+  assertEquals('menu-button-icon', model.item(5).icon);
 }
 
-function testNoRecent() {
+function testNoRecentOrLinuxFiles() {
   var volumeManager = new MockVolumeManagerWrapper();
   var shortcutListModel = new MockFolderShortcutDataModel(
       [new MockFileEntry(drive, '/root/shortcut')]);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 1d1a00b6..d660c78 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -700,6 +700,13 @@
         'volume-subtype',
         VolumeManagerCommon.getMediaViewRootTypeFromVolumeId(
             volumeInfo.volumeId));
+  } else if (
+      volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED) {
+    icon.setAttribute(
+        'volume-subtype',
+        VolumeManagerCommon.getProvidedFileSystemIdFromVolumeId(
+            volumeInfo.volumeId) ||
+            '');
   } else {
     icon.setAttribute('volume-subtype', volumeInfo.deviceType || '');
   }
@@ -1184,6 +1191,54 @@
   this.parentTree_.directoryModel.activateDirectoryEntry(this.entry);
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// SFTPMountItem
+
+/**
+ * A TreeItem which represents a directory to be mounted using SFTP.
+ *
+ * @param {!NavigationModelSFTPMountItem} modelItem
+ * @param {!DirectoryTree} tree Current tree, which contains this item.
+ * @extends {cr.ui.TreeItem}
+ * @constructor
+ */
+function SFTPMountItem(modelItem, tree) {
+  var item = new cr.ui.TreeItem();
+  item.__proto__ = SFTPMountItem.prototype;
+
+  item.parentTree_ = tree;
+  item.modelItem_ = modelItem;
+  item.innerHTML = TREE_ITEM_INNER_HTML;
+  item.label = modelItem.label;
+
+  var icon = queryRequiredElement('.icon', item);
+  icon.classList.add('item-icon');
+  icon.setAttribute('sftp-mount-icon', item.modelItem_.icon);
+
+  return item;
+}
+
+SFTPMountItem.prototype = {
+  __proto__: cr.ui.TreeItem.prototype,
+  get entry() {
+    return null;
+  },
+  get modelItem() {
+    return this.modelItem_;
+  },
+  get labelElement() {
+    return this.firstElementChild.querySelector('.label');
+  }
+};
+
+/**
+ * @override
+ */
+SFTPMountItem.prototype.handleClick = function(e) {
+  this.selected = true;
+  this.modelItem_.mount();
+};
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // DirectoryTree
@@ -1373,6 +1428,9 @@
         case NavigationModelItemType.RECENT:
           this.addAt(new RecentItem(modelItem, this), itemIndex);
           break;
+        case NavigationModelItemType.SFTP_MOUNT:
+          this.addAt(new SFTPMountItem(modelItem, this), itemIndex);
+          break;
       }
     }
     itemIndex++;
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index db3f6e59..76acb26 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -53,6 +53,7 @@
           'utm_campaign=gsg',
       IMAGE_FILE_TYPE: '$1 image',
       INSTALL_NEW_EXTENSION_LABEL: 'Install new from the webstore',
+      LINUX_FILES_ROOT_LABEL: 'Linux Files',
       MANY_ENTRIES_SELECTED: '$1 items selected',
       MANY_FILES_SELECTED: '$1 files selected',
       METADATA_BOX_ALBUM_TITLE: 'Album',
@@ -68,6 +69,8 @@
       OPEN_LABEL: 'Open',
       PLAIN_TEXT_FILE_TYPE: 'Plain text',
       PREPARING_LABEL: 'Preparing',
+      SEE_MENU_FOR_ACTIONS: 'More options available on the action bar. ' +
+          'Press Alt + A to focus the action bar.',
       SIZE_COLUMN_LABEL: 'Size',
       SPACE_AVAILABLE: '$1 available',
       STATUS_COLUMN_LABEL: 'Status',
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html
index 44ee9c54..9f85d50 100644
--- a/ui/file_manager/gallery/gallery.html
+++ b/ui/file_manager/gallery/gallery.html
@@ -15,8 +15,8 @@
   <script src="chrome://resources/js/polymer_config.js"></script>
   <script src="chrome://resources/js/util.js"></script>
 
+  <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-  <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
@@ -28,16 +28,18 @@
 
   <script src="js/gallery_scripts.js"></script>
   <style is="custom-style">
-    paper-checkbox {
-      --paper-checkbox-checked-color: white;
-      --paper-checkbox-checked-ink-color: white;
-      --paper-checkbox-checkmark-color: black;
-      --paper-checkbox-ink-size: 38px;
-      --paper-checkbox-label-color: white;
-      --paper-checkbox-label-spacing: 6px;
-      --paper-checkbox-size: 14px;
-      --paper-checkbox-unchecked-color: white;
-      --paper-checkbox-unchecked-ink-color: white;
+    cr-checkbox {
+      --cr-checkbox-checked-box-color: white;
+      --cr-checkbox-label-container: {
+        -webkit-padding-start: 6px;
+        color: white;
+      };
+      --cr-checkbox-mark-color: black;
+      --cr-checkbox-ripple-checked-color: white;
+      --cr-checkbox-ripple-size: 38px;
+      --cr-checkbox-ripple-unchecked-color: white;
+      --cr-checkbox-size: 14px;
+      --cr-checkbox-unchecked-box-color: white;
     }
     paper-input-container {
       --paper-input-container-color: rgba(255, 255, 255, 0.2);
@@ -156,9 +158,9 @@
       </div>
       <div class="edit-mode-toolbar">
         <div class="options">
-          <paper-checkbox class="overwrite-original"
+          <cr-checkbox class="overwrite-original"
                           i18n-content="GALLERY_OVERWRITE_ORIGINAL">
-          </paper-checkbox>
+          </cr-checkbox>
           <div class="saved" i18n-content="GALLERY_SAVED" hidden></div>
         </div>
         <div class="edit-bar-spacer"></div>
diff --git a/ui/file_manager/gallery/js/BUILD.gn b/ui/file_manager/gallery/js/BUILD.gn
index 03bffec..23fa7a7 100644
--- a/ui/file_manager/gallery/js/BUILD.gn
+++ b/ui/file_manager/gallery/js/BUILD.gn
@@ -139,7 +139,6 @@
     "image_editor:image_util",
     "image_editor:image_view",
     "image_editor:viewport",
-    "//third_party/polymer/v1_0/components-chromium/paper-checkbox:paper-checkbox-extracted",
     "//third_party/polymer/v1_0/components-chromium/paper-progress:paper-progress-extracted",
   ]
   externs_list = [
diff --git a/ui/file_manager/gallery/js/slide_mode.js b/ui/file_manager/gallery/js/slide_mode.js
index 36c93b8..4fdb28d 100644
--- a/ui/file_manager/gallery/js/slide_mode.js
+++ b/ui/file_manager/gallery/js/slide_mode.js
@@ -259,10 +259,10 @@
   this.savedLabel_ = queryRequiredElement('.saved', this.options_);
 
   /**
-   * @private {!PaperCheckboxElement}
+   * @private {!Element}
    * @const
    */
-  this.overwriteOriginalCheckbox_ = /** @type {!PaperCheckboxElement} */
+  this.overwriteOriginalCheckbox_ = /** @type {!Element} */
       (queryRequiredElement('.overwrite-original', this.options_));
   this.overwriteOriginalCheckbox_.addEventListener('change',
       this.onOverwriteOriginalCheckboxChanged_.bind(this));
diff --git a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
index d733754..8582c7e 100644
--- a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
+++ b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
@@ -5,7 +5,8 @@
 'use strict';
 
 /**
- * Confirms that the play button toggles play state and it's labels.
+ * Confirms that clicking the play button changes the audio player state and
+ * updates the play button's label.
  * @return {Promise} Promise to be fulfilled with on success.
  */
 testcase.togglePlayState = function() {
@@ -14,29 +15,43 @@
   return openAudio.then(function(args) {
     appId = args[0];
   }).then(function() {
-    // Audio player should start playing automatically.
+    // Audio player should start playing automatically,
     return remoteCallAudioPlayer.waitForElement(
         appId, 'audio-player[playing]');
   }).then(function() {
-    // While playing, the play/pause button should have 'Pause' label.
+    // .. and the play button label should be 'Pause'.
     return remoteCallAudioPlayer.waitForElement(
         appId, ['#play[aria-label="Pause"]']);
   }).then(function() {
-    // Clicking the pause button should change the playback state to pause.
+    // Clicking on the play button should
     return remoteCallAudioPlayer.callRemoteTestUtil(
         'fakeMouseClick', appId, ['#play']);
   }).then(function() {
+    // ... change the audio playback state to pause,
     return remoteCallAudioPlayer.waitForElement(
         appId, 'audio-player:not([playing])');
   }).then(function() {
-    // ... and the play/pause button should have 'Play' label.
+    // ... and the play button label should be 'Play'.
     return remoteCallAudioPlayer.waitForElement(
         appId, ['#play[aria-label="Play"]']);
+  }).then(function() {
+    // Clicking on the play button again should
+    return remoteCallAudioPlayer.callRemoteTestUtil(
+        'fakeMouseClick', appId, ['#play']);
+  }).then(function() {
+    // ... change the audio playback state to playing,
+    return remoteCallAudioPlayer.waitForElement(
+        appId, 'audio-player[playing]');
+  }).then(function() {
+    // ... and the play button label should be 'Pause'.
+    return remoteCallAudioPlayer.waitForElement(
+        appId, ['#play[aria-label="Pause"]']);
   });
 };
 
 /**
- * Confirms that the default volume is 50 and volume button mutes/unmutes audio.
+ * Confirms that the AudioPlayer default volume is 50 and that clicking the
+ * volume button mutes / unmutes the volume label.
  * @return {Promise} Promise to be fulfilled with on success.
  */
 testcase.changeVolumeLevel = function() {
@@ -45,11 +60,11 @@
   return openAudio.then(function(args) {
     appId = args[0];
   }).then(function() {
-    // The default volume level should be 50.
+    // The Audio Player default volume level should be 50.
     return remoteCallAudioPlayer.waitForElement(
         appId, ['control-panel[volume="50"]']);
   }).then(function() {
-    // Clicking volume button should mute the player.
+    // Clicking the volume button should mute the player.
     return remoteCallAudioPlayer.callRemoteTestUtil(
         'fakeMouseClick', appId, ['#volumeButton']);
   }).then(function() {
@@ -60,7 +75,7 @@
           appId, ['#volumeButton[aria-label="Unmute"]'])
     ]);
   }).then(function() {
-    // Clicking volume button again should restore volume.
+    // Clicking it again should unmute and restore the volume.
     return remoteCallAudioPlayer.callRemoteTestUtil(
         'fakeMouseClick', appId, ['#volumeButton']);
   }).then(function() {
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index b59886f..2837607 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -408,6 +408,9 @@
     "skia_color_space_util.cc",
     "skia_color_space_util.h",
   ]
+  deps = [
+    "//third_party/skia/third_party/skcms",
+  ]
   public_deps = [
     "//base",
     "//skia",
@@ -419,9 +422,7 @@
     ]
   }
   if (use_x11) {
-    deps = [
-      "//ui/gfx/x",
-    ]
+    deps += [ "//ui/gfx/x" ]
     configs += [ "//build/config/linux:x11" ]
   }
   defines = [ "COLOR_SPACE_IMPLEMENTATION" ]
diff --git a/ui/gfx/color_palette.h b/ui/gfx/color_palette.h
index 4a7e3db..13fcf4c 100644
--- a/ui/gfx/color_palette.h
+++ b/ui/gfx/color_palette.h
@@ -14,7 +14,7 @@
 constexpr SkColor kPlaceholderColor = SK_ColorRED;
 
 // The number refers to the shade of darkness. Each color in the MD
-// palette ranges from 50-900.
+// palette ranges from 050-900.
 constexpr SkColor kGoogleBlue300 = SkColorSetRGB(0x8A, 0xB4, 0xF8);
 constexpr SkColor kGoogleBlue500 = SkColorSetRGB(0x42, 0x85, 0xF4);
 constexpr SkColor kGoogleBlue600 = SkColorSetRGB(0x1A, 0x73, 0xE8);
@@ -34,7 +34,7 @@
 constexpr SkColor kGoogleYellow300 = SkColorSetRGB(0xFD, 0xD6, 0x63);
 constexpr SkColor kGoogleYellow700 = SkColorSetRGB(0xF2, 0x99, 0x00);
 constexpr SkColor kGoogleYellow900 = SkColorSetRGB(0xE3, 0x74, 0x00);
-constexpr SkColor kGoogleGrey50 = SkColorSetRGB(0xF8, 0xF9, 0xFA);
+constexpr SkColor kGoogleGrey050 = SkColorSetRGB(0xF8, 0xF9, 0xFA);
 constexpr SkColor kGoogleGrey100 = SkColorSetRGB(0xF1, 0xF3, 0xF4);
 constexpr SkColor kGoogleGrey200 = SkColorSetRGB(0xE8, 0xEA, 0xED);
 constexpr SkColor kGoogleGrey300 = SkColorSetRGB(0xDA, 0xDC, 0xE0);
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index a35cbd94..ab0668c 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -44,6 +44,9 @@
 
 }  // namespace
 
+// static
+int ColorSpace::kInvalidId = -1;
+
 ColorSpace::ColorSpace() {}
 
 ColorSpace::ColorSpace(PrimaryID primaries,
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 64bd12b..14dc5c73 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -158,6 +158,7 @@
 
   // Generates a process global unique ID that can be used to key a color space.
   static int GetNextId();
+  static int kInvalidId;
 
   bool operator==(const ColorSpace& other) const;
   bool operator!=(const ColorSpace& other) const;
diff --git a/ui/gfx/color_space_unittest.cc b/ui/gfx/color_space_unittest.cc
index c4a95578..fc59877 100644
--- a/ui/gfx/color_space_unittest.cc
+++ b/ui/gfx/color_space_unittest.cc
@@ -81,165 +81,6 @@
   }
 }
 
-typedef std::tuple<ColorSpace::TransferID, size_t> TableTestData;
-
-class ColorSpaceTableTest : public testing::TestWithParam<TableTestData> {};
-
-TEST_P(ColorSpaceTableTest, ApproximateTransferFn) {
-  ColorSpace::TransferID transfer_id = std::get<0>(GetParam());
-  const size_t table_size = std::get<1>(GetParam());
-
-  gfx::ColorSpace color_space(ColorSpace::PrimaryID::BT709, transfer_id);
-  SkColorSpaceTransferFn tr_fn;
-  SkColorSpaceTransferFn tr_fn_inv;
-  bool result = color_space.GetTransferFunction(&tr_fn);
-  CHECK(result);
-  color_space.GetInverseTransferFunction(&tr_fn_inv);
-
-  std::vector<float> x;
-  std::vector<float> t;
-  for (float v = 0; v <= 1.f; v += 1.f / table_size) {
-    x.push_back(v);
-    t.push_back(SkTransferFnEval(tr_fn, v));
-  }
-
-  SkColorSpaceTransferFn fn_approx;
-  bool converged =
-      SkApproximateTransferFn(x.data(), t.data(), x.size(), &fn_approx);
-  EXPECT_TRUE(converged);
-
-  for (size_t i = 0; i < x.size(); ++i) {
-    float fn_approx_of_x = SkTransferFnEval(fn_approx, x[i]);
-    EXPECT_NEAR(t[i], fn_approx_of_x, 3.f / 256.f);
-    if (std::abs(t[i] - fn_approx_of_x) > 3.f / 256.f)
-      break;
-  }
-}
-
-ColorSpace::TransferID all_transfers[] = {
-    ColorSpace::TransferID::GAMMA22,   ColorSpace::TransferID::GAMMA24,
-    ColorSpace::TransferID::GAMMA28,   ColorSpace::TransferID::BT709,
-    ColorSpace::TransferID::SMPTE240M, ColorSpace::TransferID::IEC61966_2_1,
-    ColorSpace::TransferID::LINEAR};
-
-size_t all_table_sizes[] = {512, 256, 128, 64, 16, 11, 8, 7, 6, 5, 4};
-
-INSTANTIATE_TEST_CASE_P(A,
-                        ColorSpaceTableTest,
-                        testing::Combine(testing::ValuesIn(all_transfers),
-                                         testing::ValuesIn(all_table_sizes)));
-
-TEST(ColorSpace, ApproximateTransferFnClamped) {
-  // These data represent a transfer function that is clamped at the high
-  // end of its domain. It comes from the color profile attached to
-  // https://crbug.com/750459
-  float t[] = {
-      0.000000f, 0.000305f, 0.000610f, 0.000916f, 0.001221f, 0.001511f,
-      0.001816f, 0.002121f, 0.002426f, 0.002731f, 0.003037f, 0.003601f,
-      0.003937f, 0.004303f, 0.004685f, 0.005081f, 0.005509f, 0.005951f,
-      0.006409f, 0.006882f, 0.007385f, 0.007904f, 0.008438f, 0.009003f,
-      0.009583f, 0.010193f, 0.010819f, 0.011460f, 0.012131f, 0.012818f,
-      0.013535f, 0.014267f, 0.015030f, 0.015808f, 0.016617f, 0.017456f,
-      0.018296f, 0.019181f, 0.020081f, 0.021012f, 0.021958f, 0.022934f,
-      0.023926f, 0.024949f, 0.026001f, 0.027070f, 0.028168f, 0.029297f,
-      0.030442f, 0.031617f, 0.032822f, 0.034058f, 0.035309f, 0.036591f,
-      0.037903f, 0.039231f, 0.040604f, 0.041993f, 0.043412f, 0.044846f,
-      0.046326f, 0.047822f, 0.049348f, 0.050904f, 0.052491f, 0.054108f,
-      0.055756f, 0.057420f, 0.059113f, 0.060853f, 0.062608f, 0.064393f,
-      0.066209f, 0.068055f, 0.069932f, 0.071839f, 0.073762f, 0.075731f,
-      0.077729f, 0.079759f, 0.081804f, 0.083894f, 0.086015f, 0.088167f,
-      0.090333f, 0.092546f, 0.094789f, 0.097063f, 0.099367f, 0.101701f,
-      0.104067f, 0.106477f, 0.108904f, 0.111360f, 0.113863f, 0.116381f,
-      0.118944f, 0.121538f, 0.124163f, 0.126818f, 0.129519f, 0.132235f,
-      0.134997f, 0.137789f, 0.140612f, 0.143465f, 0.146365f, 0.149279f,
-      0.152239f, 0.155230f, 0.158267f, 0.161318f, 0.164416f, 0.167544f,
-      0.170718f, 0.173907f, 0.177142f, 0.180407f, 0.183719f, 0.187045f,
-      0.190433f, 0.193835f, 0.197284f, 0.200763f, 0.204273f, 0.207813f,
-      0.211398f, 0.215030f, 0.218692f, 0.222385f, 0.226108f, 0.229877f,
-      0.233677f, 0.237522f, 0.241382f, 0.245304f, 0.249256f, 0.253239f,
-      0.257252f, 0.261311f, 0.265415f, 0.269551f, 0.273716f, 0.277928f,
-      0.282170f, 0.286458f, 0.290776f, 0.295140f, 0.299535f, 0.303975f,
-      0.308446f, 0.312947f, 0.317494f, 0.322087f, 0.326711f, 0.331380f,
-      0.336080f, 0.340826f, 0.345602f, 0.350423f, 0.355291f, 0.360174f,
-      0.365118f, 0.370092f, 0.375113f, 0.380163f, 0.385260f, 0.390387f,
-      0.395560f, 0.400778f, 0.406027f, 0.411322f, 0.416663f, 0.422034f,
-      0.427451f, 0.432898f, 0.438392f, 0.443931f, 0.449500f, 0.455116f,
-      0.460777f, 0.466468f, 0.472221f, 0.477989f, 0.483818f, 0.489677f,
-      0.495583f, 0.501518f, 0.507500f, 0.513527f, 0.519600f, 0.525719f,
-      0.531868f, 0.538064f, 0.544289f, 0.550576f, 0.556893f, 0.563256f,
-      0.569650f, 0.576104f, 0.582589f, 0.589120f, 0.595697f, 0.602304f,
-      0.608972f, 0.615671f, 0.622415f, 0.629206f, 0.636027f, 0.642908f,
-      0.649821f, 0.656779f, 0.663783f, 0.670832f, 0.677913f, 0.685054f,
-      0.692226f, 0.699443f, 0.706706f, 0.714015f, 0.721370f, 0.728771f,
-      0.736202f, 0.743694f, 0.751217f, 0.758785f, 0.766400f, 0.774060f,
-      0.781765f, 0.789517f, 0.797314f, 0.805158f, 0.813031f, 0.820966f,
-      0.828946f, 0.836957f, 0.845029f, 0.853132f, 0.861280f, 0.869490f,
-      0.877729f, 0.886015f, 0.894362f, 0.902739f, 0.911162f, 0.919631f,
-      0.928161f, 0.936721f, 0.945327f, 0.953994f, 0.962692f, 0.971435f,
-      0.980240f, 0.989075f, 0.997955f, 1.000000f,
-  };
-  std::vector<float> x;
-  for (size_t v = 0; v < 256; ++v)
-    x.push_back(v / 255.f);
-
-  SkColorSpaceTransferFn fn_approx;
-  bool converged = SkApproximateTransferFn(x.data(), t, x.size(), &fn_approx);
-  EXPECT_TRUE(converged);
-
-  // The approximation should be nearly exact.
-  float expected_error = 1.f / 4096.f;
-  for (size_t i = 0; i < x.size(); ++i) {
-    float fn_approx_of_x = SkTransferFnEval(fn_approx, x[i]);
-    EXPECT_NEAR(t[i], fn_approx_of_x, expected_error);
-    if (std::abs(t[i] - fn_approx_of_x) > expected_error)
-      break;
-  }
-}
-
-TEST(ColorSpace, ApproximateTransferFnBadMatch) {
-  const float kStep = 1.f / 512.f;
-  ColorSpace::TransferID transfer_ids[3] = {
-      ColorSpace::TransferID::IEC61966_2_1, ColorSpace::TransferID::GAMMA22,
-      ColorSpace::TransferID::BT709,
-  };
-  gfx::ColorSpace color_spaces[3];
-
-  // The first iteration will have a perfect match. The second will be very
-  // close. The third will converge, but with an error of ~7/256.
-  for (size_t transfers_to_use = 1; transfers_to_use <= 3; ++transfers_to_use) {
-    std::vector<float> x;
-    std::vector<float> t;
-    for (size_t c = 0; c < transfers_to_use; ++c) {
-      color_spaces[c] =
-          gfx::ColorSpace(ColorSpace::PrimaryID::BT709, transfer_ids[c]);
-      SkColorSpaceTransferFn tr_fn;
-      bool result = color_spaces[c].GetTransferFunction(&tr_fn);
-      CHECK(result);
-
-      for (float v = 0; v <= 1.f; v += kStep) {
-        x.push_back(v);
-        t.push_back(SkTransferFnEval(tr_fn, v));
-      }
-    }
-
-    SkColorSpaceTransferFn fn_approx;
-    bool converged =
-        SkApproximateTransferFn(x.data(), t.data(), x.size(), &fn_approx);
-    EXPECT_TRUE(converged);
-
-    float expected_error = 1.5f / 256.f;
-    if (transfers_to_use == 3)
-      expected_error = 8.f / 256.f;
-
-    for (size_t i = 0; i < x.size(); ++i) {
-      float fn_approx_of_x = SkTransferFnEval(fn_approx, x[i]);
-      EXPECT_NEAR(t[i], fn_approx_of_x, expected_error);
-      if (std::abs(t[i] - fn_approx_of_x) > expected_error)
-        break;
-    }
-  }
-}
-
 TEST(ColorSpace, RasterAndBlend) {
   ColorSpace display_color_space;
 
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index c4c31f83..59407f4e 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -265,21 +265,21 @@
   ColorTransform::TriStim expected_transformed_value(
       0.34090986847877502f, 0.42633286118507385f, 0.3408740758895874f);
 
-  // One step should be needed, namely, the SkColorSpaceXform.
+  // Two steps should be needed, transfer fn and matrix.
   std::unique_ptr<ColorTransform> icc_to_xyzd50(
       ColorTransform::NewColorTransform(
           icc_space, xyzd50, ColorTransform::Intent::INTENT_ABSOLUTE));
-  EXPECT_EQ(icc_to_xyzd50->NumberOfStepsForTesting(), 1u);
+  EXPECT_EQ(icc_to_xyzd50->NumberOfStepsForTesting(), 2u);
   icc_to_xyzd50->Transform(&transformed_value, 1);
   EXPECT_NEAR(transformed_value.x(), expected_transformed_value.x(), kEpsilon);
   EXPECT_NEAR(transformed_value.y(), expected_transformed_value.y(), kEpsilon);
   EXPECT_NEAR(transformed_value.z(), expected_transformed_value.z(), kEpsilon);
 
-  // One step should be needed, namely, the SkColorSpaceXform.
+  // Two steps should be needed, matrix and transfer fn.
   std::unique_ptr<ColorTransform> xyzd50_to_icc(
       ColorTransform::NewColorTransform(
           xyzd50, icc_space, ColorTransform::Intent::INTENT_ABSOLUTE));
-  EXPECT_EQ(xyzd50_to_icc->NumberOfStepsForTesting(), 1u);
+  EXPECT_EQ(xyzd50_to_icc->NumberOfStepsForTesting(), 2u);
   xyzd50_to_icc->Transform(&transformed_value, 1);
   EXPECT_NEAR(input_value.x(), transformed_value.x(), kEpsilon);
   EXPECT_NEAR(input_value.y(), transformed_value.y(), kEpsilon);
diff --git a/ui/gfx/icc_profile.cc b/ui/gfx/icc_profile.cc
index 55b7a6d..a5f60e5 100644
--- a/ui/gfx/icc_profile.cc
+++ b/ui/gfx/icc_profile.cc
@@ -13,7 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/synchronization/lock.h"
 #include "third_party/skia/include/core/SkColorSpaceXform.h"
-#include "third_party/skia/include/core/SkICC.h"
+#include "third_party/skia/third_party/skcms/skcms.h"
 #include "ui/gfx/skia_color_space_util.h"
 
 namespace gfx {
@@ -75,73 +75,36 @@
   if (data_.empty())
     return kICCNoProfile;
 
-  // Parse the profile and attempt to create a SkColorSpaceXform out of it.
-  sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB();
-  sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size());
-  if (!sk_icc) {
-    DLOG(ERROR) << "Failed to parse ICC profile to SkICC.";
+  // Parse the profile.
+  skcms_ICCProfile profile;
+  if (!skcms_Parse(data_.data(), data_.size(), &profile)) {
+    DLOG(ERROR) << "Failed to parse ICC profile.";
     return kICCFailedToParse;
   }
-  sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size());
-  if (!sk_color_space_) {
-    DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace.";
-    return kICCFailedToExtractSkColorSpace;
-  }
-  std::unique_ptr<SkColorSpaceXform> sk_color_space_xform =
-      SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get());
-  if (!sk_color_space_xform) {
-    DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform.";
-    return kICCFailedToCreateXform;
+
+  // Coerce it into a rasterization destination (if possible). If the profile
+  // can't be approximated accurately, skcms will not allow transforming to it,
+  // and this will fail.
+  if (!skcms_MakeUsableAsDestinationWithSingleCurve(&profile)) {
+    DLOG(ERROR) << "Parsed ICC profile but can't make usable as destination.";
+    return kICCFailedToMakeUsable;
   }
 
-  // Because this SkColorSpace can be used to construct a transform, we can use
-  // it to create a LUT based color transform, at the very least. If we fail to
-  // get any better approximation, we'll use sRGB as our approximation.
-  ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_);
-  ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_);
+  // Create an SkColorSpace from the profile. This should always succeed after
+  // calling MakeUsableAsDestinationWithSingleCurve.
+  sk_color_space_ = SkColorSpace::Make(&profile);
+  DCHECK(sk_color_space_);
 
-  // If our SkColorSpace representation is sRGB then return that.
-  if (sk_color_space_->isSRGB())
-    return kICCExtractedSRGBColorSpace;
+  // Extract the primary matrix and transfer function
+  to_XYZD50_.set3x3RowMajorf(&profile.toXYZD50.vals[0][0]);
+  memcpy(&transfer_fn_, &profile.trc[0].parametric, sizeof(transfer_fn_));
 
-  // A primary matrix is required for our parametric representations. Use it if
-  // it exists.
-  SkMatrix44 to_XYZD50_matrix;
-  if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) {
-    DLOG(ERROR) << "Failed to extract ICC profile primary matrix.";
-    return kICCFailedToExtractMatrix;
-  }
-  to_XYZD50_ = to_XYZD50_matrix;
-
-  // Try to directly extract a numerical transfer function. Use it if it
-  // exists.
-  SkColorSpaceTransferFn exact_tr_fn;
-  if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) {
-    transfer_fn_ = exact_tr_fn;
-    return kICCExtractedMatrixAndAnalyticTrFn;
-  }
-
-  // Attempt to fit a parametric transfer function to the table data in the
-  // profile.
-  SkColorSpaceTransferFn approx_tr_fn;
-  if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) {
-    DLOG(ERROR) << "Failed approximate transfer function.";
-    return kICCFailedToConvergeToApproximateTrFn;
-  }
-
-  // If this converged, but has too high error, use the sRGB transfer function
-  // from above.
-  const float kMaxError = 2.f / 256.f;
-  if (transfer_fn_error_ >= kMaxError) {
-    DLOG(ERROR) << "Failed to accurately approximate transfer function, error: "
-                << 256.f * transfer_fn_error_ << "/256";
-    return kICCFailedToApproximateTrFnAccurately;
-  };
-
-  // If the error is sufficiently low, declare that the approximation is
-  // accurate.
-  transfer_fn_ = approx_tr_fn;
-  return kICCExtractedMatrixAndApproximatedTrFn;
+  // We assume that if we accurately approximated the profile, then the
+  // single-curve version (which may have higher error) is also okay. If we
+  // want to maintain the distinction between accurate and inaccurate profiles,
+  // we could check to see if the single-curve version is/ approximately equal
+  // to the original (or to the multi-channel approximation).
+  return kICCExtractedMatrixAndTrFn;
 }
 
 ICCProfile::ICCProfile() = default;
@@ -293,25 +256,14 @@
   // Parse the ICC profile
   analyze_result_ = Initialize();
   switch (analyze_result_) {
-    case kICCExtractedSRGBColorSpace:
-    case kICCExtractedMatrixAndAnalyticTrFn:
-    case kICCExtractedMatrixAndApproximatedTrFn:
+    case kICCExtractedMatrixAndTrFn:
       // Successfully and accurately extracted color space.
       is_valid_ = true;
       is_parametric_ = true;
       break;
-    case kICCFailedToConvergeToApproximateTrFn:
-    case kICCFailedToApproximateTrFnAccurately:
-      // Successfully but extracted a color space, but it isn't accurate enough.
-      is_valid_ = true;
-      is_parametric_ = false;
-      break;
-    case kICCFailedToExtractRawTrFn:
-    case kICCFailedToExtractMatrix:
     case kICCFailedToParse:
-    case kICCFailedToExtractSkColorSpace:
-    case kICCFailedToCreateXform:
     case kICCNoProfile:
+    case kICCFailedToMakeUsable:
       // Can't even use this color space as a LUT.
       is_valid_ = false;
       is_parametric_ = false;
@@ -352,23 +304,7 @@
   histogrammed_display_ids_.insert(display_id);
 
   UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult",
-                            analyze_result_, kICCProfileAnalyzeLast);
-
-  // Add histograms for numerical approximation.
-  bool nonlinear_fit_converged =
-      analyze_result_ == kICCExtractedMatrixAndApproximatedTrFn ||
-      analyze_result_ == kICCFailedToApproximateTrFnAccurately;
-  bool nonlinear_fit_did_not_converge =
-      analyze_result_ == kICCFailedToConvergeToApproximateTrFn;
-  if (nonlinear_fit_converged || nonlinear_fit_did_not_converge) {
-    UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged",
-                          nonlinear_fit_converged);
-  }
-  if (nonlinear_fit_converged) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Blink.ColorSpace.Destination.NonlinearFitError",
-        static_cast<int>(transfer_fn_error_ * 255), 0, 127, 16);
-  }
+                            analyze_result_);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/icc_profile.h b/ui/gfx/icc_profile.h
index 198781d..825d813 100644
--- a/ui/gfx/icc_profile.h
+++ b/ui/gfx/icc_profile.h
@@ -77,18 +77,11 @@
 
     // This must match ICCProfileAnalyzeResult enum in histograms.xml.
     enum AnalyzeResult {
-      kICCExtractedMatrixAndAnalyticTrFn = 0,
-      kICCExtractedMatrixAndApproximatedTrFn = 1,
-      kICCFailedToConvergeToApproximateTrFn = 2,
-      kICCFailedToExtractRawTrFn = 3,
-      kICCFailedToExtractMatrix = 4,
       kICCFailedToParse = 5,
-      kICCFailedToExtractSkColorSpace = 6,
-      kICCFailedToCreateXform = 7,
-      kICCFailedToApproximateTrFnAccurately = 8,
-      kICCExtractedSRGBColorSpace = 9,
       kICCNoProfile = 10,
-      kICCProfileAnalyzeLast = kICCNoProfile,
+      kICCFailedToMakeUsable = 11,
+      kICCExtractedMatrixAndTrFn = 12,
+      kMaxValue = kICCExtractedMatrixAndTrFn,
     };
 
     const std::vector<char> data_;
@@ -118,11 +111,6 @@
     SkMatrix44 to_XYZD50_;
     SkColorSpaceTransferFn transfer_fn_;
 
-    // The L-infinity error of the parametric color space fit. This is undefined
-    // unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or
-    // kICCExtractedMatrixAndApproximatedTrFn.
-    float transfer_fn_error_ = 0;
-
     // The set of display ids which have have caused this ICC profile to be
     // recorded in UMA histograms. Only record an ICC profile once per display
     // id (since the same profile will be re-read repeatedly, e.g, when displays
diff --git a/ui/gfx/icc_profile_unittest.cc b/ui/gfx/icc_profile_unittest.cc
index 7e9b3272..6e3cc9d8 100644
--- a/ui/gfx/icc_profile_unittest.cc
+++ b/ui/gfx/icc_profile_unittest.cc
@@ -67,30 +67,20 @@
 }
 
 TEST(ICCProfile, ParametricVersusExactInaccurate) {
-  // This ICC profile has three transfer functions that differ enough that the
-  // parametric color space is considered inaccurate.
+  // This ICC profile has three transfer functions that differ significantly,
+  // but ICCProfiles are always either invalid or considered accurate (and in
+  // this case, each curve is approximated, so the profile is "accurate").
+  // See comments in ICCProfile::Internals::Analyze.
   ICCProfile multi_tr_fn = ICCProfileForTestingNoAnalyticTrFn();
-  EXPECT_FALSE(multi_tr_fn.GetColorSpace().IsParametricAccurate());
+  EXPECT_TRUE(multi_tr_fn.GetColorSpace().IsParametricAccurate());
 
-  // Fails to get parametric color space because the space is not parametric.
   ICCProfile profile;
-  profile = ICCProfile::FromParametricColorSpace(multi_tr_fn.GetColorSpace());
-  EXPECT_FALSE(profile.IsValid());
-
-  // The Mac cache does not find the parametric approximation, because the cache
-  // only has the original.
-  profile = ICCProfile::FromCacheMac(
-      multi_tr_fn.GetColorSpace().GetParametricApproximation());
-  EXPECT_FALSE(profile.IsValid());
-
-  // The Mac cache does find the original.
   profile = ICCProfile::FromCacheMac(multi_tr_fn.GetColorSpace());
   EXPECT_TRUE(profile.IsValid());
   EXPECT_EQ(profile, multi_tr_fn);
 
   // We are capable of generating a parametric approximation.
-  profile = ICCProfile::FromParametricColorSpace(
-      multi_tr_fn.GetColorSpace().GetParametricApproximation());
+  profile = ICCProfile::FromParametricColorSpace(multi_tr_fn.GetColorSpace());
   EXPECT_TRUE(profile.IsValid());
   EXPECT_NE(profile, multi_tr_fn);
 }
@@ -127,13 +117,12 @@
 }
 
 TEST(ICCProfile, ParametricVersusExactA2B) {
-  // This ICC profile has only an A2B representation. We cannot create an
-  // SkColorSpaceXform to A2B only ICC profiles, so this should be marked
-  // as invalid.
+  // This ICC profile has only an A2B representation. We cannot transform to
+  // A2B only ICC profiles, so this should be marked as invalid.
   ICCProfile a2b = ICCProfileForTestingA2BOnly();
   EXPECT_FALSE(a2b.GetColorSpace().IsValid());
 
-  // Even though it is invalid, it should not be equal to the empty constructor.
+  // Even though it is invalid, it should not be equal to the empty constructor
   EXPECT_NE(a2b, gfx::ICCProfile());
 }
 
diff --git a/ui/gfx/mac/io_surface.cc b/ui/gfx/mac/io_surface.cc
index bf6111f..056d0525 100644
--- a/ui/gfx/mac/io_surface.cc
+++ b/ui/gfx/mac/io_surface.cc
@@ -183,13 +183,9 @@
     return nullptr;
   }
 
-  // For unknown reasons, triggering this lock on OS X 10.9, on certain GPUs,
-  // causes PDFs to render incorrectly. Hopefully this check can be removed once
-  // pdfium switches to a Skia backend on Mac.
-  // https://crbug.com/594343.
   // IOSurface clearing causes significant performance regression on about half
   // of all devices running Yosemite. https://crbug.com/606850#c22.
-  if (base::mac::IsOS10_9() || base::mac::IsOS10_10())
+  if (base::mac::IsOS10_10())
     should_clear = false;
 
   if (should_clear) {
diff --git a/ui/gfx/platform_font_mac_unittest.mm b/ui/gfx/platform_font_mac_unittest.mm
index a935e945..f031568 100644
--- a/ui/gfx/platform_font_mac_unittest.mm
+++ b/ui/gfx/platform_font_mac_unittest.mm
@@ -151,16 +151,6 @@
   }
 
   ns_font = [NSFont systemFontOfSize:13];
-  if (base::mac::IsOS10_9()) {
-    // On 10.9 the system font doesn't provide finer-grained weights. It's
-    // either bold or it isn't.
-    for (int row = 6; row <= 14; ++row) {
-      SCOPED_TRACE(testing::Message() << "Row: " << row);
-      ns_font = [manager convertWeight:up ofFont:ns_font];
-      EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight());
-    }
-    return;
-  }
 
   if (base::mac::IsOS10_11()) {
     // On 10.11 the API jumps to BOLD, but has heavier weights as well.
diff --git a/ui/gfx/skia_color_space_util.cc b/ui/gfx/skia_color_space_util.cc
index b840f7e6..8f7b6f1d 100644
--- a/ui/gfx/skia_color_space_util.cc
+++ b/ui/gfx/skia_color_space_util.cc
@@ -12,287 +12,6 @@
 
 namespace gfx {
 
-namespace {
-
-// Evaluate the gradient of the nonlinear component of fn
-void SkTransferFnEvalGradientNonlinear(const SkColorSpaceTransferFn& fn,
-                                       float x,
-                                       float* d_fn_d_fA_at_x,
-                                       float* d_fn_d_fB_at_x,
-                                       float* d_fn_d_fE_at_x,
-                                       float* d_fn_d_fG_at_x) {
-  float base = fn.fA * x + fn.fB;
-  if (base > 0.f) {
-    *d_fn_d_fA_at_x = fn.fG * x * std::pow(base, fn.fG - 1.f);
-    *d_fn_d_fB_at_x = fn.fG * std::pow(base, fn.fG - 1.f);
-    *d_fn_d_fE_at_x = 1.f;
-    *d_fn_d_fG_at_x = std::pow(base, fn.fG) * std::log(base);
-  } else {
-    *d_fn_d_fA_at_x = 0.f;
-    *d_fn_d_fB_at_x = 0.f;
-    *d_fn_d_fE_at_x = 0.f;
-    *d_fn_d_fG_at_x = 0.f;
-  }
-}
-
-// Take one Gauss-Newton step updating fA, fB, fE, and fG, given fD.
-bool SkTransferFnGaussNewtonStepNonlinear(SkColorSpaceTransferFn* fn,
-                                          float* error_Linfty_after,
-                                          const float* x,
-                                          const float* t,
-                                          size_t n) {
-  // Let ne_lhs be the left hand side of the normal equations, and let ne_rhs
-  // be the right hand side. Zero the diagonal of |ne_lhs| and all of |ne_rhs|.
-  SkMatrix44 ne_lhs;
-  SkVector4 ne_rhs;
-  for (int row = 0; row < 4; ++row) {
-    for (int col = 0; col < 4; ++col) {
-      ne_lhs.set(row, col, 0);
-    }
-    ne_rhs.fData[row] = 0;
-  }
-
-  // Add the contributions from each sample to the normal equations.
-  for (size_t i = 0; i < n; ++i) {
-    // Ignore points in the linear segment.
-    if (x[i] < fn->fD)
-      continue;
-
-    // Let J be the gradient of fn with respect to parameters A, B, E, and G,
-    // evaulated at this point.
-    float J[4];
-    SkTransferFnEvalGradientNonlinear(*fn, x[i], &J[0], &J[1], &J[2], &J[3]);
-    // Let r be the residual at this point;
-    float r = t[i] - SkTransferFnEval(*fn, x[i]);
-
-    // Update the normal equations left hand side with the outer product of J
-    // with itself.
-    for (int row = 0; row < 4; ++row) {
-      for (int col = 0; col < 4; ++col) {
-        ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]);
-      }
-
-      // Update the normal equations right hand side the product of J with the
-      // residual
-      ne_rhs.fData[row] += J[row] * r;
-    }
-  }
-
-  // Note that if fG = 1, then the normal equations will be singular (because,
-  // when fG = 1, because fB and fE are equivalent parameters).  To avoid
-  // problems, fix fE (row/column 3) in these circumstances.
-  float kEpsilonForG = 1.f / 1024.f;
-  if (std::abs(fn->fG - 1) < kEpsilonForG) {
-    for (int row = 0; row < 4; ++row) {
-      float value = (row == 2) ? 1.f : 0.f;
-      ne_lhs.set(row, 2, value);
-      ne_lhs.set(2, row, value);
-    }
-    ne_rhs.fData[2] = 0.f;
-  }
-
-  // Solve the normal equations.
-  SkMatrix44 ne_lhs_inv;
-  if (!ne_lhs.invert(&ne_lhs_inv))
-    return false;
-  SkVector4 step = ne_lhs_inv * ne_rhs;
-
-  // Update the transfer function.
-  fn->fA += step.fData[0];
-  fn->fB += step.fData[1];
-  fn->fE += step.fData[2];
-  fn->fG += step.fData[3];
-
-  // fA should always be positive.
-  fn->fA = std::max(fn->fA, 0.f);
-
-  // Ensure that fn be defined at fD.
-  if (fn->fA * fn->fD + fn->fB < 0.f)
-    fn->fB = -fn->fA * fn->fD;
-
-  // Compute the Linfinity error.
-  *error_Linfty_after = 0;
-  for (size_t i = 0; i < n; ++i) {
-    if (x[i] >= fn->fD) {
-      float error = std::abs(t[i] - SkTransferFnEval(*fn, x[i]));
-      *error_Linfty_after = std::max(error, *error_Linfty_after);
-    }
-  }
-
-  return true;
-}
-
-// Solve for fA, fB, fE, and fG, given fD. The initial value of |fn| is the
-// point from which iteration starts.
-bool SkTransferFnSolveNonlinear(SkColorSpaceTransferFn* fn,
-                                const float* x,
-                                const float* t,
-                                size_t n) {
-  // Take a maximum of 8 Gauss-Newton steps.
-  const size_t kNumSteps = 8;
-
-  // The L-infinity error after each step.
-  float step_error[kNumSteps] = {0};
-  size_t step = 0;
-  for (;; ++step) {
-    // If the normal equations are singular, we can't continue.
-    if (!SkTransferFnGaussNewtonStepNonlinear(fn, &step_error[step], x, t, n))
-      return false;
-
-    // If the error is inf or nan, we are clearly not converging.
-    if (std::isnan(step_error[step]) || std::isinf(step_error[step]))
-      return false;
-
-    // Stop if our error is tiny.
-    float kEarlyOutTinyErrorThreshold = (1.f / 16.f) / 256.f;
-    if (step_error[step] < kEarlyOutTinyErrorThreshold)
-      break;
-
-    // Stop if our error is not changing, or changing in the wrong direction.
-    if (step > 1) {
-      // If our error is is huge for two iterations, we're probably not in the
-      // region of convergence.
-      if (step_error[step] > 1.f && step_error[step - 1] > 1.f)
-        return false;
-
-      // If our error didn't change by ~1%, assume we've converged as much as we
-      // are going to.
-      const float kEarlyOutByPercentChangeThreshold = 32.f / 256.f;
-      const float kMinimumPercentChange = 1.f / 128.f;
-      float percent_change =
-          std::abs(step_error[step] - step_error[step - 1]) / step_error[step];
-      if (percent_change < kMinimumPercentChange &&
-          step_error[step] < kEarlyOutByPercentChangeThreshold) {
-        break;
-      }
-    }
-    if (step == kNumSteps - 1)
-      break;
-  }
-
-  // Declare failure if our error is obviously too high.
-  float kDidNotConvergeThreshold = 64.f / 256.f;
-  if (step_error[step] > kDidNotConvergeThreshold)
-    return false;
-
-  // We've converged to a reasonable solution. If some of the parameters are
-  // extremely close to 0 or 1, set them to 0 or 1.
-  const float kRoundEpsilon = 1.f / 1024.f;
-  if (std::abs(fn->fA - 1.f) < kRoundEpsilon)
-    fn->fA = 1.f;
-  if (std::abs(fn->fB) < kRoundEpsilon)
-    fn->fB = 0;
-  if (std::abs(fn->fE) < kRoundEpsilon)
-    fn->fE = 0;
-  if (std::abs(fn->fG - 1.f) < kRoundEpsilon)
-    fn->fG = 1.f;
-  return true;
-}
-
-bool SkApproximateTransferFnInternal(const float* x,
-                                     const float* t,
-                                     size_t n,
-                                     SkColorSpaceTransferFn* fn) {
-  // First, guess at a value of fD. Assume that the nonlinear segment applies
-  // to all x >= 0.15. This is generally a safe assumption (fD is usually less
-  // than 0.1).
-  const float kLinearSegmentMaximum = 0.15f;
-  fn->fD = kLinearSegmentMaximum;
-
-  // Do a nonlinear regression on the nonlinear segment. Use a number of guesses
-  // for the initial value of fG, because not all values will converge.
-  bool nonlinear_fit_converged = false;
-  {
-    const size_t kNumInitialGammas = 4;
-    float initial_gammas[kNumInitialGammas] = {2.2f, 1.f, 3.f, 0.5f};
-    for (size_t i = 0; i < kNumInitialGammas; ++i) {
-      fn->fG = initial_gammas[i];
-      fn->fA = 1;
-      fn->fB = 0;
-      fn->fC = 1;
-      fn->fE = 0;
-      fn->fF = 0;
-      if (SkTransferFnSolveNonlinear(fn, x, t, n)) {
-        nonlinear_fit_converged = true;
-        break;
-      }
-    }
-  }
-  if (!nonlinear_fit_converged)
-    return false;
-
-  // Now walk back fD from our initial guess to the point where our nonlinear
-  // fit no longer fits (or all the way to 0 if it fits).
-  {
-    // Find the L-infinity error of this nonlinear fit (using our old fD value).
-    float max_error_in_nonlinear_fit = 0;
-    for (size_t i = 0; i < n; ++i) {
-      if (x[i] < fn->fD)
-        continue;
-      float error_at_xi = std::abs(t[i] - SkTransferFnEval(*fn, x[i]));
-      max_error_in_nonlinear_fit =
-          std::max(max_error_in_nonlinear_fit, error_at_xi);
-    }
-
-    // Now find the maximum x value where this nonlinear fit is no longer
-    // accurate, no longer defined, or no longer nonnegative.
-    fn->fD = 0.f;
-    float max_x_where_nonlinear_does_not_fit = -1.f;
-    for (size_t i = 0; i < n; ++i) {
-      if (x[i] >= kLinearSegmentMaximum)
-        continue;
-
-      // The nonlinear segment is only undefined when fA * x + fB is
-      // nonnegative.
-      float fn_at_xi = -1;
-      if (fn->fA * x[i] + fn->fB >= 0)
-        fn_at_xi = SkTransferFnEvalUnclamped(*fn, x[i]);
-
-      // If the value is negative (or undefined), say that the fit was bad.
-      bool nonlinear_fits_xi = true;
-      if (fn_at_xi < 0)
-        nonlinear_fits_xi = false;
-
-      // Compute the error, and define "no longer accurate" as "has more than
-      // 10% more error than the maximum error in the fit segment".
-      if (nonlinear_fits_xi) {
-        float error_at_xi = std::abs(t[i] - fn_at_xi);
-        if (error_at_xi > 1.1f * max_error_in_nonlinear_fit)
-          nonlinear_fits_xi = false;
-      }
-
-      if (!nonlinear_fits_xi) {
-        max_x_where_nonlinear_does_not_fit =
-            std::max(max_x_where_nonlinear_does_not_fit, x[i]);
-      }
-    }
-
-    // Now let fD be the highest sample of x that is above the threshold where
-    // the nonlinear segment does not fit.
-    fn->fD = 1.f;
-    for (size_t i = 0; i < n; ++i) {
-      if (x[i] > max_x_where_nonlinear_does_not_fit)
-        fn->fD = std::min(fn->fD, x[i]);
-    }
-  }
-
-  // Compute the linear segment, now that we have our definitive fD.
-  if (fn->fD <= 0) {
-    // If this has no linear segment, don't try to solve for one.
-    fn->fC = 1;
-    fn->fF = 0;
-  } else {
-    // Set the linear portion such that it go through the origin and be
-    // continuous with the nonlinear segment.
-    float fn_at_fD = SkTransferFnEval(*fn, fn->fD);
-    fn->fC = fn_at_fD / fn->fD;
-    fn->fF = 0;
-  }
-  return true;
-}
-
-}  // namespace
-
 float SkTransferFnEvalUnclamped(const SkColorSpaceTransferFn& fn, float x) {
   if (x < fn.fD)
     return fn.fC * x + fn.fF;
@@ -345,62 +64,6 @@
   return true;
 }
 
-bool SkApproximateTransferFn(const float* x,
-                             const float* t,
-                             size_t n,
-                             SkColorSpaceTransferFn* fn) {
-  if (SkApproximateTransferFnInternal(x, t, n, fn))
-    return true;
-  fn->fA = 1;
-  fn->fB = 0;
-  fn->fC = 1;
-  fn->fD = 0;
-  fn->fE = 0;
-  fn->fF = 0;
-  fn->fG = 1;
-  return false;
-}
-
-bool SkApproximateTransferFn(sk_sp<SkICC> sk_icc,
-                             float* max_error,
-                             SkColorSpaceTransferFn* fn) {
-  SkICC::Tables tables;
-  bool got_tables = sk_icc->rawTransferFnData(&tables);
-  if (!got_tables)
-    return false;
-
-  // Merge all channels' tables into a single array.
-  SkICC::Channel* channels[3] = {&tables.fGreen, &tables.fRed, &tables.fBlue};
-  std::vector<float> x;
-  std::vector<float> t;
-  for (size_t c = 0; c < 3; ++c) {
-    SkICC::Channel* channel = channels[c];
-    const float* data = reinterpret_cast<const float*>(
-        tables.fStorage->bytes() + channel->fOffset);
-    for (int i = 0; i < channel->fCount; ++i) {
-      float xi = i / (channel->fCount - 1.f);
-      float ti = data[i];
-      x.push_back(xi);
-      t.push_back(ti);
-    }
-  }
-
-  // Approximate the transfer function.
-  bool converged =
-      SkApproximateTransferFnInternal(x.data(), t.data(), x.size(), fn);
-  if (!converged)
-    return false;
-
-  // Compute the error among all channels.
-  *max_error = 0;
-  for (size_t i = 0; i < x.size(); ++i) {
-    float fn_of_xi = gfx::SkTransferFnEval(*fn, x[i]);
-    float error_at_xi = std::abs(t[i] - fn_of_xi);
-    *max_error = std::max(*max_error, error_at_xi);
-  }
-  return true;
-}
-
 bool SkMatrixIsApproximatelyIdentity(const SkMatrix44& m) {
   const float kEpsilon = 1.f / 256.f;
   for (int i = 0; i < 4; ++i) {
diff --git a/ui/gfx/skia_color_space_util.h b/ui/gfx/skia_color_space_util.h
index f15f8267..bec9a2f 100644
--- a/ui/gfx/skia_color_space_util.h
+++ b/ui/gfx/skia_color_space_util.h
@@ -31,21 +31,6 @@
 bool COLOR_SPACE_EXPORT
 SkTransferFnIsApproximatelyIdentity(const SkColorSpaceTransferFn& fn);
 
-// Approximates the |n| points in |x| and |t| by the transfer function |fn|.
-// Returns true if the approximation converged.
-bool COLOR_SPACE_EXPORT SkApproximateTransferFn(const float* x,
-                                                const float* t,
-                                                size_t n,
-                                                SkColorSpaceTransferFn* fn);
-
-// Approximates |sk_icc| by the transfer function |fn|. Returns in |max_error|
-// the maximum pointwise of all color channels' transfer functions with |fn|.
-// Returns false if no approximation was possible, or no approximation
-// converged.
-bool COLOR_SPACE_EXPORT SkApproximateTransferFn(sk_sp<SkICC> sk_icc,
-                                                float* max_error,
-                                                SkColorSpaceTransferFn* fn);
-
 bool COLOR_SPACE_EXPORT SkMatrixIsApproximatelyIdentity(const SkMatrix44& m);
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image_dxgi.cc b/ui/gl/gl_image_dxgi.cc
index 612dde30..f35f264 100644
--- a/ui/gl/gl_image_dxgi.cc
+++ b/ui/gl/gl_image_dxgi.cc
@@ -237,13 +237,10 @@
 
   HRESULT hr = d3d11_device_->CreateTexture2D(
       &desc, nullptr, decoder_copy_texture_.GetAddressOf());
-  // TODO(sunnyps): Remove after fixing https://crbug.com/794735
-  base::debug::Alias(&hr);
-  HRESULT reason_hr = S_OK;
-  base::debug::Alias(&reason_hr);
-  if (hr == DXGI_ERROR_DEVICE_REMOVED)
-    reason_hr = d3d11_device_->GetDeviceRemovedReason();
-  CHECK(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "CreateTexture2D failed: " << std::hex << hr;
+    return false;
+  }
   EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
 
   EGLAttrib frame_attributes[] = {
@@ -253,20 +250,27 @@
   EGLBoolean result = eglStreamPostD3DTextureANGLE(
       egl_display, stream_, static_cast<void*>(decoder_copy_texture_.Get()),
       frame_attributes);
-  if (!result)
+  if (!result) {
+    DLOG(ERROR) << "eglStreamPostD3DTextureANGLE failed";
     return false;
+  }
   result = eglStreamConsumerAcquireKHR(egl_display, stream_);
-  if (!result)
+  if (!result) {
+    DLOG(ERROR) << "eglStreamConsumerAcquireKHR failed";
     return false;
+  }
 
   d3d11_device_.CopyTo(video_device_.GetAddressOf());
   Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
   d3d11_device_->GetImmediateContext(context.GetAddressOf());
   context.CopyTo(video_context_.GetAddressOf());
 
+#if DCHECK_IS_ON()
   Microsoft::WRL::ComPtr<ID3D10Multithread> multithread;
   d3d11_device_.CopyTo(multithread.GetAddressOf());
-  CHECK(multithread->GetMultithreadProtected());
+  DCHECK(multithread->GetMultithreadProtected());
+#endif  // DCHECK_IS_ON()
+
   return true;
 }
 
@@ -277,7 +281,7 @@
 
   Microsoft::WRL::ComPtr<ID3D11Device> processor_device;
   video_processor->GetDevice(processor_device.GetAddressOf());
-  CHECK_EQ(d3d11_device_.Get(), processor_device.Get());
+  DCHECK_EQ(d3d11_device_.Get(), processor_device.Get());
 
   d3d11_processor_ = video_processor;
   enumerator_ = enumerator;
@@ -303,10 +307,10 @@
   if (copied_)
     return true;
 
-  CHECK(video_device_);
+  DCHECK(video_device_);
   Microsoft::WRL::ComPtr<ID3D11Device> texture_device;
   texture_->GetDevice(texture_device.GetAddressOf());
-  CHECK_EQ(d3d11_device_.Get(), texture_device.Get());
+  DCHECK_EQ(d3d11_device_.Get(), texture_device.Get());
 
   D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_view_desc = {0};
   input_view_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
diff --git a/ui/gl/test/gl_image_test_template.h b/ui/gl/test/gl_image_test_template.h
index 87f4cba..a9fa046 100644
--- a/ui/gl/test/gl_image_test_template.h
+++ b/ui/gl/test/gl_image_test_template.h
@@ -145,11 +145,6 @@
     return;
 
 #if defined(OS_MACOSX)
-  // This functionality is disabled on Mavericks because it breaks PDF
-  // rendering. https://crbug.com/594343.
-  if (base::mac::IsOS10_9())
-    return;
-
   // This functionality is disabled on Yosemite because it is suspected of
   // causing performance regressions on old hardware. https://crbug.com/606850.
   if (base::mac::IsOS10_10())
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 33d7e8a..a016b777 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -89,6 +89,10 @@
   static const SkColor kBlueButtonTextColor = SK_ColorWHITE;
   static const SkColor kBlueButtonShadowColor = SkColorSetRGB(0x53, 0x8C, 0xEA);
   // MenuItem:
+  static const SkColor kTouchableMenuItemLabelColor =
+      SkColorSetRGB(0x20, 0x21, 0x24);
+  static const SkColor kActionableSubmenuVerticalSeparatorColor =
+      SkColorSetARGB(0x24, 0x20, 0x21, 0x24);
   static const SkColor kMenuBackgroundColor = SK_ColorWHITE;
   static const SkColor kMenuHighlightBackgroundColor =
       SkColorSetA(SK_ColorBLACK, 0x14);
@@ -187,6 +191,10 @@
       return kDisabledTextColor;
 
     // MenuItem
+    case NativeTheme::kColorId_TouchableMenuItemLabelColor:
+      return kTouchableMenuItemLabelColor;
+    case NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor:
+      return kActionableSubmenuVerticalSeparatorColor;
     case NativeTheme::kColorId_SelectedMenuItemForegroundColor:
       return kSelectedMenuItemForegroundColor;
     case NativeTheme::kColorId_MenuBorderColor:
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index d926149c..c459020 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -311,6 +311,8 @@
     kColorId_ProminentButtonColor,
     kColorId_TextOnProminentButtonColor,
     // MenuItem
+    kColorId_TouchableMenuItemLabelColor,
+    kColorId_ActionableSubmenuVerticalSeparatorColor,
     kColorId_EnabledMenuItemForegroundColor,
     kColorId_DisabledMenuItemForegroundColor,
     kColorId_SelectedMenuItemForegroundColor,
diff --git a/ui/native_theme/native_theme_dark_aura.cc b/ui/native_theme/native_theme_dark_aura.cc
index 655b487..998c2454 100644
--- a/ui/native_theme/native_theme_dark_aura.cc
+++ b/ui/native_theme/native_theme_dark_aura.cc
@@ -112,6 +112,8 @@
     case kColorId_DisabledMenuItemForegroundColor:
     case kColorId_SelectedMenuItemForegroundColor:
     case kColorId_FocusedMenuItemBackgroundColor:
+    case kColorId_TouchableMenuItemLabelColor:
+    case kColorId_ActionableSubmenuVerticalSeparatorColor:
     case kColorId_MenuItemMinorTextColor:
     case kColorId_MenuSeparatorColor:
     case kColorId_MenuBackgroundColor:
diff --git a/ui/ozone/platform/drm/host/drm_device_connector.cc b/ui/ozone/platform/drm/host/drm_device_connector.cc
index 6bfc3cb..eb9fec9 100644
--- a/ui/ozone/platform/drm/host/drm_device_connector.cc
+++ b/ui/ozone/platform/drm/host/drm_device_connector.cc
@@ -42,9 +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_ && features::IsMusEnabled()) ||
-         (!ws_runner_ && !features::IsMusEnabled()));
+  // Invariant: we only have a runner at startup if executing in mash mode.
+  DCHECK((ws_runner_ && features::IsMashEnabled()) ||
+         (!ws_runner_ && !features::IsMashEnabled()));
 }
 
 DrmDeviceConnector::~DrmDeviceConnector() {}
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 09632b1..c06c013d 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
@@ -95,7 +95,7 @@
   if (ui_runner_) {
     weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
   } else {
-    DCHECK(!features::IsMusEnabled());
+    DCHECK(!features::IsMashEnabled());
   }
 }
 
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index 6ebe4075..25b285c 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -194,8 +194,6 @@
 
 void WaylandWindow::Minimize() {
   DCHECK(xdg_surface_);
-
-  DCHECK(xdg_surface_);
   xdg_surface_->SetMinimized();
   connection_->ScheduleFlush();
 
diff --git a/ui/views/controls/menu/menu_config.cc b/ui/views/controls/menu/menu_config.cc
index a0f7af8..1a22946d 100644
--- a/ui/views/controls/menu/menu_config.cc
+++ b/ui/views/controls/menu/menu_config.cc
@@ -29,7 +29,7 @@
       icon_to_label_padding(10),
       touchable_icon_to_label_padding(22),
       touchable_icon_size(20),
-      touchable_icon_color(SkColorSetA(SK_ColorBLACK, 0xDE)),
+      touchable_icon_color(SkColorSetRGB(0x5F, 0x63, 0x60)),
       check_width(kMenuCheckSize),
       check_height(kMenuCheckSize),
       arrow_width(kSubmenuArrowSize),
@@ -39,9 +39,14 @@
       separator_spacing_height(3),
       separator_thickness(1),
       show_mnemonics(false),
+      use_mnemonics(true),
       scroll_arrow_height(3),
       label_to_minor_text_padding(10),
       item_min_height(0),
+      actionable_submenu_arrow_to_edge_padding(14),
+      actionable_submenu_width(37),
+      actionable_submenu_vertical_separator_height(18),
+      actionable_submenu_vertical_separator_width(1),
       show_accelerators(true),
       always_use_icon_to_label_padding(false),
       align_arrow_and_shortcut(false),
diff --git a/ui/views/controls/menu/menu_config.h b/ui/views/controls/menu/menu_config.h
index 4f4d83c0..22a7d9eb 100644
--- a/ui/views/controls/menu/menu_config.h
+++ b/ui/views/controls/menu/menu_config.h
@@ -109,6 +109,9 @@
   // Are mnemonics shown?
   bool show_mnemonics;
 
+  // Are mnemonics used to activate items?
+  bool use_mnemonics;
+
   // Height of the scroll arrow.
   int scroll_arrow_height;
 
@@ -119,6 +122,18 @@
   // Minimum height of menu item.
   int item_min_height;
 
+  // Edge padding for an actionable submenu arrow.
+  int actionable_submenu_arrow_to_edge_padding;
+
+  // Width of the submenu in an actionable submenu.
+  int actionable_submenu_width;
+
+  // The height of the vertical separator used in an actionable submenu.
+  int actionable_submenu_vertical_separator_height;
+
+  // The width of the vertical separator used in an actionable submenu.
+  int actionable_submenu_vertical_separator_width;
+
   // Whether the keyboard accelerators are visible.
   bool show_accelerators;
 
diff --git a/ui/views/controls/menu/menu_config_mac.mm b/ui/views/controls/menu/menu_config_mac.mm
index 16757bb..d308d1e0 100644
--- a/ui/views/controls/menu/menu_config_mac.mm
+++ b/ui/views/controls/menu/menu_config_mac.mm
@@ -48,6 +48,7 @@
   font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0]));
   check_selected_combobox_item = true;
   arrow_key_selection_wraps = false;
+  use_mnemonics = false;
   if (ui::MaterialDesignController::IsSecondaryUiMaterial())
     InitMaterialMenuConfig(this);
 }
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index a59f1f2..5ee38baa4 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -694,7 +694,7 @@
   // for selected folder menu items. If it's only a left click, show the
   // contents of the folder.
   if (!part.is_scroll() && part.menu &&
-      !(part.menu->HasSubmenu() &&
+      !(part.should_submenu_show && part.menu->HasSubmenu() &&
         (event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
     if (active_mouse_view_tracker_->view()) {
       SendMouseReleaseToActiveView(source, event);
@@ -819,7 +819,7 @@
     }
   } else if (event->type() == ui::ET_GESTURE_TAP) {
     if (!part.is_scroll() && part.menu &&
-        !(part.menu->HasSubmenu())) {
+        !(part.should_submenu_show && part.menu->HasSubmenu())) {
       if (part.menu->GetDelegate()->IsTriggerableEvent(
           part.menu, *event)) {
         item_selected_by_touch_ = true;
@@ -1165,7 +1165,14 @@
   size_t current_size = current_path.size();
   size_t new_size = new_path.size();
 
-  bool pending_item_changed = pending_state_.item != menu_item;
+  // ACTIONABLE_SUBMENUs can change without changing the pending item, this
+  // occurs when selection moves from the COMMAND area to the SUBMENU area of
+  // the ACTIONABLE_SUBMENU.
+  const bool pending_item_changed =
+      pending_state_.item != menu_item ||
+      pending_state_.submenu_open !=
+          !!(selection_types & SELECTION_OPEN_SUBMENU);
+
   if (pending_item_changed && pending_state_.item)
     SetHotTrackedButton(nullptr);
 
@@ -1174,7 +1181,8 @@
       current_path.empty() ? NULL : current_path.front()->GetDelegate();
   for (size_t i = paths_differ_at; i < current_size; ++i) {
     if (current_delegate &&
-        current_path[i]->GetType() == MenuItemView::SUBMENU) {
+        (current_path[i]->GetType() == MenuItemView::SUBMENU ||
+         current_path[i]->GetType() == MenuItemView::ACTIONABLE_SUBMENU)) {
       current_delegate->WillHideMenu(current_path[i]);
     }
     current_path[i]->SetSelected(false);
@@ -1184,6 +1192,14 @@
   for (size_t i = paths_differ_at; i < new_size; ++i) {
     new_path[i]->ScrollRectToVisible(new_path[i]->GetLocalBounds());
     new_path[i]->SetSelected(true);
+    if (new_path[i]->GetType() == MenuItemView::ACTIONABLE_SUBMENU) {
+      new_path[i]->SetSelectionOfActionableSubmenu(
+          (selection_types & SELECTION_OPEN_SUBMENU) != 0);
+    }
+  }
+  if (menu_item && menu_item->GetType() == MenuItemView::ACTIONABLE_SUBMENU) {
+    menu_item->SetSelectionOfActionableSubmenu(
+        (selection_types & SELECTION_OPEN_SUBMENU) != 0);
   }
 
   if (menu_item && menu_item->GetDelegate())
@@ -1206,9 +1222,10 @@
     StartShowTimer();
 
   // Notify an accessibility focus event on all menu items except for the root.
-  if (menu_item &&
-      (MenuDepth(menu_item) != 1 ||
-       menu_item->GetType() != MenuItemView::SUBMENU)) {
+  if (menu_item && (MenuDepth(menu_item) != 1 ||
+                    menu_item->GetType() != MenuItemView::SUBMENU ||
+                    (menu_item->GetType() == MenuItemView::ACTIONABLE_SUBMENU &&
+                     (selection_types & SELECTION_OPEN_SUBMENU) == 0))) {
     menu_item->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   }
 }
@@ -1255,7 +1272,7 @@
       possible_drag_ = true;
       press_pt_ = event->location();
     }
-    if (part.menu->HasSubmenu())
+    if (part.menu->HasSubmenu() && part.should_submenu_show)
       selection_types |= SELECTION_OPEN_SUBMENU;
   }
   SetSelection(part.menu, selection_types);
@@ -1693,11 +1710,19 @@
     part->menu = GetMenuItemAt(menu, menu_loc.x(), menu_loc.y());
     part->type = MenuPart::MENU_ITEM;
     part->submenu = menu;
+    part->should_submenu_show =
+        part->submenu && part->menu &&
+        (part->menu->GetType() == MenuItemView::SUBMENU ||
+         IsLocationOverSubmenuAreaOfActionableSubmenu(part->menu, screen_loc));
     if (!part->menu)
       part->parent = menu->GetMenuItem();
     return true;
   }
 
+  // Return false for points on touchable menu shadows, to search parent menus.
+  if (use_touchable_layout_)
+    return false;
+
   // While the mouse isn't over a menu item or the scroll buttons of menu, it
   // is contained by menu and so we return true. If we didn't return true other
   // menus would be searched, even though they are likely obscured by us.
@@ -1729,7 +1754,20 @@
   gfx::Point view_loc = screen_loc;
   View::ConvertPointFromScreen(submenu, &view_loc);
   gfx::Rect vis_rect = submenu->GetVisibleBounds();
-  return vis_rect.Contains(view_loc.x(), view_loc.y());
+  return vis_rect.Contains(view_loc);
+}
+
+bool MenuController::IsLocationOverSubmenuAreaOfActionableSubmenu(
+    MenuItemView* item,
+    const gfx::Point& screen_loc) const {
+  if (!item || item->GetType() != MenuItemView::ACTIONABLE_SUBMENU)
+    return false;
+
+  gfx::Point view_loc = screen_loc;
+  View::ConvertPointFromScreen(item, &view_loc);
+  if (base::i18n::IsRTL())
+    view_loc.set_x(item->GetMirroredXInView(view_loc.x()));
+  return item->GetSubmenuAreaOfActionableSubmenu().Contains(view_loc);
 }
 
 void MenuController::CommitPendingSelection() {
@@ -2121,7 +2159,6 @@
                                                     bool prefer_leading,
                                                     bool* is_leading) {
   DCHECK(item);
-  DCHECK(!item->GetParentMenuItem());
 
   // Assume we can honor prefer_leading.
   *is_leading = prefer_leading;
@@ -2130,103 +2167,153 @@
   DCHECK(submenu);
 
   gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
-  const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
-
-  // First the size gets reduced to the possible space.
-  if (!state_.monitor_bounds.IsEmpty()) {
-    int max_width = state_.monitor_bounds.width();
-    int max_height = state_.monitor_bounds.height();
-    // In case of bubbles, the maximum width is limited by the space
-    // between the display corner and the target area + the tip size.
-    if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
-      max_width = owner_bounds.x() - state_.monitor_bounds.x() +
-                  kBubbleTipSizeLeftRight;
-    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
-      max_width = state_.monitor_bounds.right() - owner_bounds.right() +
-                  kBubbleTipSizeLeftRight;
-    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
-      max_height = owner_bounds.y() - state_.monitor_bounds.y() +
-                   kBubbleTipSizeTopBottom;
-    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
-      max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
-                   kBubbleTipSizeTopBottom;
-    }
-    // The space for the menu to cover should never get empty.
-    DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
-    DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
-    pref.set_width(std::min(pref.width(), max_width));
-    pref.set_height(std::min(pref.height(), max_height));
-  }
-  // Also make sure that the menu does not go too wide.
-  pref.set_width(std::min(pref.width(),
-                          item->GetDelegate()->GetMaxWidthForMenu(item)));
-
+  int x = 0;
+  int y = 0;
   const MenuConfig& menu_config = MenuConfig::instance();
   // Shadow insets are built into MenuScrollView's preferred size so it must be
   // compensated for when determining the bounds of touchable menus.
-  gfx::Insets shadow_insets = BubbleBorder::GetBorderAndShadowInsets(
-      menu_config.touchable_menu_shadow_elevation);
+  const gfx::Insets border_and_shadow_insets =
+      BubbleBorder::GetBorderAndShadowInsets(
+          menu_config.touchable_menu_shadow_elevation);
 
-  int x, y;
-  if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
-      state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
-    if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
-      y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
-    else
-      y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
+  if (!item->GetParentMenuItem()) {
+    // This is a top-level menu, position it relative to the anchor bounds.
+    const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
 
-    x = owner_bounds.CenterPoint().x() - pref.width() / 2;
-    int x_old = x;
-    if (x < state_.monitor_bounds.x()) {
-      x = state_.monitor_bounds.x();
-    } else if (x + pref.width() > state_.monitor_bounds.right()) {
-      x = state_.monitor_bounds.right() - pref.width();
+    // First the size gets reduced to the possible space.
+    if (!state_.monitor_bounds.IsEmpty()) {
+      int max_width = state_.monitor_bounds.width();
+      int max_height = state_.monitor_bounds.height();
+      // In case of bubbles, the maximum width is limited by the space
+      // between the display corner and the target area + the tip size.
+      if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
+        max_width = owner_bounds.x() - state_.monitor_bounds.x() +
+                    kBubbleTipSizeLeftRight;
+      } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
+        max_width = state_.monitor_bounds.right() - owner_bounds.right() +
+                    kBubbleTipSizeLeftRight;
+      } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
+        max_height = owner_bounds.y() - state_.monitor_bounds.y() +
+                     kBubbleTipSizeTopBottom;
+      } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
+        max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
+                     kBubbleTipSizeTopBottom;
+      }
+      // The menu should always have a non-empty available area.
+      DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
+      DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
+      pref.set_width(std::min(pref.width(), max_width));
+      pref.set_height(std::min(pref.height(), max_height));
     }
-    submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
-        pref.width() / 2 - x + x_old);
-  } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
-    // Align the left edges of the menu and anchor, and the bottom of the menu
-    // with the top of the anchor.
-    x = owner_bounds.origin().x() - shadow_insets.left();
-    y = owner_bounds.origin().y() - pref.height() + shadow_insets.bottom() -
-        menu_config.touchable_anchor_offset;
-    // Align the right of the container with the right of the app icon.
-    if (x + pref.width() > state_.monitor_bounds.width())
-      x = owner_bounds.right() - pref.width() + shadow_insets.right();
-    // Align the top of the menu with the bottom of the anchor.
-    if (y < 0) {
-      y = owner_bounds.bottom() - shadow_insets.top() +
+    // Respect the delegate's maximum width.
+    pref.set_width(
+        std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item)));
+
+    if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
+        state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
+      if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
+        y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
+      else
+        y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
+
+      x = owner_bounds.CenterPoint().x() - pref.width() / 2;
+      int x_old = x;
+      if (x < state_.monitor_bounds.x())
+        x = state_.monitor_bounds.x();
+      else if (x + pref.width() > state_.monitor_bounds.right())
+        x = state_.monitor_bounds.right() - pref.width();
+      submenu->GetScrollViewContainer()->SetBubbleArrowOffset(pref.width() / 2 -
+                                                              x + x_old);
+    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
+      // Align the left edges of the menu and anchor, and the bottom of the menu
+      // with the top of the anchor.
+      x = owner_bounds.origin().x() - border_and_shadow_insets.left();
+      y = owner_bounds.origin().y() - pref.height() +
+          border_and_shadow_insets.bottom() -
           menu_config.touchable_anchor_offset;
-    }
-  } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
-    // Align the right of the menu with the left of the anchor, and the top of
-    // the menu with the top of the anchor.
-    x = owner_bounds.origin().x() - pref.width() + shadow_insets.right() -
-        menu_config.touchable_anchor_offset;
-    y = owner_bounds.origin().y() - shadow_insets.top();
-    // Align the left of the menu with the right of the anchor.
-    if (x < 0) {
-      x = owner_bounds.right() + shadow_insets.left() +
+      // Align the right of the container with the right of the anchor.
+      if (x + pref.width() > state_.monitor_bounds.width()) {
+        x = owner_bounds.right() - pref.width() +
+            border_and_shadow_insets.right();
+      }
+      // Align the top of the menu with the bottom of the anchor.
+      if (y < 0) {
+        y = owner_bounds.bottom() - border_and_shadow_insets.top() +
+            menu_config.touchable_anchor_offset;
+      }
+    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
+      // Align the right of the menu with the left of the anchor, and the top of
+      // the menu with the top of the anchor.
+      x = owner_bounds.origin().x() - pref.width() +
+          border_and_shadow_insets.right() -
           menu_config.touchable_anchor_offset;
+      y = owner_bounds.origin().y() - border_and_shadow_insets.top();
+      // Align the left of the menu with the right of the anchor.
+      if (x < 0) {
+        x = owner_bounds.right() - border_and_shadow_insets.left() +
+            menu_config.touchable_anchor_offset;
+      }
+      // Align the bottom of the menu to the bottom of the anchor.
+      if (y + pref.height() > state_.monitor_bounds.height()) {
+        y = owner_bounds.bottom() - pref.height() +
+            border_and_shadow_insets.bottom();
+      }
+    } else {
+      if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
+        x = owner_bounds.right() - kBubbleTipSizeLeftRight;
+      else
+        x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
+
+      y = owner_bounds.CenterPoint().y() - pref.height() / 2;
+      int y_old = y;
+      if (y < state_.monitor_bounds.y())
+        y = state_.monitor_bounds.y();
+      else if (y + pref.height() > state_.monitor_bounds.bottom())
+        y = state_.monitor_bounds.bottom() - pref.height();
+      submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
+          pref.height() / 2 - y + y_old);
     }
-    // Align the bottom of the menu to the bottom of the anchor.
-    if (y + pref.height() > state_.monitor_bounds.height())
-      y = owner_bounds.bottom() - pref.height() + shadow_insets.bottom();
   } else {
-    if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
-      x = owner_bounds.right() - kBubbleTipSizeLeftRight;
-    else
-      x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
-
-    y = owner_bounds.CenterPoint().y() - pref.height() / 2;
-    int y_old = y;
-    if (y < state_.monitor_bounds.y()) {
-      y = state_.monitor_bounds.y();
-    } else if (y + pref.height() > state_.monitor_bounds.bottom()) {
-      y = state_.monitor_bounds.bottom() - pref.height();
+    if (!use_touchable_layout_) {
+      NOTIMPLEMENTED()
+          << "Nested bubble menus are only implemented for touchable menus.";
     }
-    submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
-        pref.height() / 2 - y + y_old);
+
+    // This is a sub-menu, position it relative to the parent menu.
+    const gfx::Rect item_bounds = item->GetBoundsInScreen();
+    // If the layout is RTL, then a 'leading' menu is positioned to the left of
+    // the parent menu item and not to the right.
+    const bool layout_is_rtl = base::i18n::IsRTL();
+    const bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
+                                     (!prefer_leading && layout_is_rtl);
+    if (create_on_the_right) {
+      x = item_bounds.right() - border_and_shadow_insets.left();
+      if (state_.monitor_bounds.width() != 0 &&
+          (x + menu_config.touchable_menu_width -
+               border_and_shadow_insets.right() >
+           state_.monitor_bounds.right())) {
+        *is_leading = prefer_leading;
+        x = item_bounds.x() - menu_config.touchable_menu_width -
+            border_and_shadow_insets.right();
+      }
+    } else {
+      x = item_bounds.x() - menu_config.touchable_menu_width -
+          border_and_shadow_insets.right();
+      if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
+        *is_leading = !prefer_leading;
+        x = item_bounds.x() + menu_config.touchable_menu_width -
+            border_and_shadow_insets.left();
+      }
+    }
+    y = item_bounds.y() - border_and_shadow_insets.top() -
+        menu_config.vertical_touchable_menu_item_padding;
+    if (y + pref.height() - border_and_shadow_insets.bottom() >
+        state_.monitor_bounds.bottom()) {
+      y = state_.monitor_bounds.bottom() - pref.height() +
+          border_and_shadow_insets.top();
+    }
+    if (y < state_.monitor_bounds.y())
+      y = state_.monitor_bounds.y() - border_and_shadow_insets.top();
   }
   return gfx::Rect(x, y, pref.width(), pref.height());
 }
@@ -2722,7 +2809,8 @@
     return;
 
   if (part.type == MenuPart::MENU_ITEM && part.menu) {
-    SetSelection(part.menu, SELECTION_OPEN_SUBMENU);
+    SetSelection(part.menu, part.should_submenu_show ? SELECTION_OPEN_SUBMENU
+                                                     : SELECTION_DEFAULT);
   } else if (!part.is_scroll() && pending_state_.item &&
              pending_state_.item->GetParentMenuItem() &&
              !pending_state_.item->SubmenuIsShowing()) {
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index b208044..563b4b26 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -294,13 +294,11 @@
       SCROLL_DOWN
     };
 
-    MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {}
-
     // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
     bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }
 
     // Type of part.
-    Type type;
+    Type type = NONE;
 
     // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
     // this is NULL.
@@ -308,14 +306,17 @@
     //       but is over a menu (for example, the mouse is over a separator or
     //       empty menu), this is NULL and parent is the menu the mouse was
     //       clicked on.
-    MenuItemView* menu;
+    MenuItemView* menu = nullptr;
 
     // If type is MENU_ITEM but the mouse is not over a menu item this is the
     // parent of the menu item the user clicked on. Otherwise this is NULL.
-    MenuItemView* parent;
+    MenuItemView* parent = nullptr;
 
     // This is the submenu the mouse is over.
-    SubmenuView* submenu;
+    SubmenuView* submenu = nullptr;
+
+    // Whether the controller should apply SELECTION_OPEN_SUBMENU to this item.
+    bool should_submenu_show = false;
   };
 
   // Sets the selection to |menu_item|. A value of NULL unselects
@@ -419,6 +420,11 @@
   bool DoesSubmenuContainLocation(SubmenuView* submenu,
                                   const gfx::Point& screen_loc);
 
+  // Returns whether the location is over the ACTIONABLE_SUBMENU's submenu area.
+  bool IsLocationOverSubmenuAreaOfActionableSubmenu(
+      MenuItemView* item,
+      const gfx::Point& screen_loc) const;
+
   // Opens/Closes the necessary menus such that state_ matches that of
   // pending_state_. This is invoked if submenus are not opened immediately,
   // but after a delay.
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 9db91f9..4eb50c9 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -12,6 +12,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/menu_model.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rect.h"
@@ -29,6 +30,7 @@
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/menu_separator.h"
 #include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/controls/separator.h"
 #include "ui/views/widget/widget.h"
 
 namespace views {
@@ -171,6 +173,7 @@
 
   switch (GetType()) {
     case SUBMENU:
+    case ACTIONABLE_SUBMENU:
       node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
       break;
     case CHECKBOX:
@@ -267,7 +270,7 @@
   item->SetMinorIcon(minor_icon);
   if (!icon.isNull())
     item->SetIcon(icon);
-  if (type == SUBMENU)
+  if (type == SUBMENU || type == ACTIONABLE_SUBMENU)
     item->CreateSubmenu();
   if (GetDelegate() && !GetDelegate()->IsCommandVisible(item_id))
     item->SetVisible(false);
@@ -398,6 +401,19 @@
   SchedulePaint();
 }
 
+void MenuItemView::SetSelectionOfActionableSubmenu(
+    bool submenu_area_of_actionable_submenu_selected) {
+  DCHECK_EQ(ACTIONABLE_SUBMENU, type_);
+  if (submenu_area_of_actionable_submenu_selected_ ==
+      submenu_area_of_actionable_submenu_selected) {
+    return;
+  }
+
+  submenu_area_of_actionable_submenu_selected_ =
+      submenu_area_of_actionable_submenu_selected;
+  SchedulePaint();
+}
+
 void MenuItemView::SetTooltip(const base::string16& tooltip, int item_id) {
   MenuItemView* item = GetMenuItemByID(item_id);
   DCHECK(item);
@@ -458,6 +474,13 @@
   return height;
 }
 
+gfx::Rect MenuItemView::GetSubmenuAreaOfActionableSubmenu() const {
+  DCHECK_EQ(ACTIONABLE_SUBMENU, type_);
+  const MenuConfig& config = MenuConfig::instance();
+  return gfx::Rect(gfx::Point(vertical_separator_->bounds().right(), 0),
+                   gfx::Size(config.actionable_submenu_width, height()));
+}
+
 const MenuItemView::MenuItemDimensions& MenuItemView::GetDimensions() const {
   if (!is_dimensions_valid())
     dimensions_ = CalculateDimensions();
@@ -495,8 +518,10 @@
 }
 
 base::char16 MenuItemView::GetMnemonic() {
-  if (!GetRootMenuItem()->has_mnemonics_)
+  if (!GetRootMenuItem()->has_mnemonics_ ||
+      !MenuConfig::instance().use_mnemonics) {
     return 0;
+  }
 
   size_t index = 0;
   do {
@@ -580,6 +605,8 @@
         continue;
       if (submenu_arrow_image_view_ == child)
         continue;
+      if (vertical_separator_ == child)
+        continue;
       int width = child->GetPreferredSize().width();
       child->SetBounds(x - width, 0, width, height());
       x -= width + kChildXPadding;
@@ -611,13 +638,25 @@
     }
 
     if (submenu_arrow_image_view_) {
-      int x = width() - config.arrow_width - config.arrow_to_edge_padding;
+      int x = width() - config.arrow_width -
+              (type_ == ACTIONABLE_SUBMENU
+                   ? config.actionable_submenu_arrow_to_edge_padding
+                   : config.arrow_to_edge_padding);
       int y =
           (height() + GetTopMargin() - GetBottomMargin() - kSubmenuArrowSize) /
           2;
       submenu_arrow_image_view_->SetBounds(x, y, config.arrow_width,
                                            kSubmenuArrowSize);
     }
+
+    if (vertical_separator_) {
+      const gfx::Size preferred_size = vertical_separator_->GetPreferredSize();
+      int x = width() - config.actionable_submenu_width -
+              config.actionable_submenu_vertical_separator_width;
+      int y = (height() - preferred_size.height()) / 2;
+      vertical_separator_->SetBoundsRect(
+          gfx::Rect(gfx::Point(x, y), preferred_size));
+    }
   }
 }
 
@@ -705,15 +744,16 @@
                         MenuItemView::Type type,
                         MenuDelegate* delegate) {
   delegate_ = delegate;
-  controller_ = NULL;
+  controller_ = nullptr;
   canceled_ = false;
   parent_menu_item_ = parent;
   type_ = type;
   selected_ = false;
   command_ = command;
-  submenu_ = NULL;
+  submenu_ = nullptr;
   radio_check_image_view_ = nullptr;
   submenu_arrow_image_view_ = nullptr;
+  vertical_separator_ = nullptr;
   show_mnemonics_ = false;
   // Assign our ID, this allows SubmenuItemView to find MenuItemViews.
   set_id(kMenuItemViewID);
@@ -729,6 +769,20 @@
     AddChildView(radio_check_image_view_);
   }
 
+  if (type_ == ACTIONABLE_SUBMENU) {
+    vertical_separator_ = new Separator();
+    vertical_separator_->SetVisible(true);
+    vertical_separator_->SetFocusBehavior(FocusBehavior::NEVER);
+    const MenuConfig& config = MenuConfig::instance();
+    vertical_separator_->SetColor(GetNativeTheme()->GetSystemColor(
+        ui::NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor));
+    vertical_separator_->SetPreferredSize(
+        gfx::Size(config.actionable_submenu_vertical_separator_width,
+                  config.actionable_submenu_vertical_separator_height));
+    vertical_separator_->set_can_process_events_within_subtree(false);
+    AddChildView(vertical_separator_);
+  }
+
   if (submenu_arrow_image_view_)
     submenu_arrow_image_view_->SetVisible(HasSubmenu());
 
@@ -844,6 +898,15 @@
   ui::NativeTheme* native_theme = GetNativeTheme();
   if (render_selection) {
     gfx::Rect item_bounds(0, 0, width(), height());
+    if (type_ == ACTIONABLE_SUBMENU) {
+      if (submenu_area_of_actionable_submenu_selected_) {
+        item_bounds = GetSubmenuAreaOfActionableSubmenu();
+      } else {
+        item_bounds = gfx::Rect(gfx::Size(
+            width() - MenuConfig::instance().actionable_submenu_width - 1,
+            height()));
+      }
+    }
     AdjustBoundsForRTLUI(&item_bounds);
 
     native_theme->Paint(canvas->sk_canvas(),
@@ -962,6 +1025,10 @@
     if (!emphasized)
       color_id = ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor;
   }
+
+  if (GetMenuController() && GetMenuController()->use_touchable_layout())
+    color_id = ui::NativeTheme::kColorId_TouchableMenuItemLabelColor;
+
   return GetNativeTheme()->GetSystemColor(color_id);
 }
 
@@ -1012,6 +1079,8 @@
       continue;
     if (submenu_arrow_image_view_ == child)
       continue;
+    if (vertical_separator_ == child)
+      continue;
     if (i)
       width += kChildXPadding;
     width += child->GetPreferredSize().width();
@@ -1153,7 +1222,7 @@
   // not the number of menu items.
   return child_count() - (icon_view_ ? 1 : 0) -
          (radio_check_image_view_ ? 1 : 0) -
-         (submenu_arrow_image_view_ ? 1 : 0);
+         (submenu_arrow_image_view_ ? 1 : 0) - (vertical_separator_ ? 1 : 0);
 }
 
 int MenuItemView::GetMaxIconViewWidth() const {
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 8037459..6276932 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -17,7 +17,6 @@
 #include "build/build_config.h"
 #include "ui/base/models/menu_separator_types.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_types.h"
 #include "ui/views/view.h"
@@ -45,6 +44,7 @@
 
 class MenuController;
 class MenuDelegate;
+class Separator;
 class TestMenuItemView;
 class SubmenuView;
 
@@ -83,15 +83,16 @@
   // ID used to identify empty menu items.
   static const int kEmptyMenuItemViewID;
 
-  // Different types of menu items.  EMPTY is a special type for empty
-  // menus that is only used internally.
+  // Different types of menu items.
   enum Type {
-    NORMAL,
-    SUBMENU,
-    CHECKBOX,
-    RADIO,
-    SEPARATOR,
-    EMPTY
+    NORMAL,              // Performs an action when selected.
+    SUBMENU,             // Presents a submenu within another menu.
+    ACTIONABLE_SUBMENU,  // A SUBMENU that is also a COMMAND.
+    CHECKBOX,            // Can be selected/checked to toggle a boolean state.
+    RADIO,               // Can be selected/checked among a group of choices.
+    SEPARATOR,           // Shows a horizontal line separator.
+    EMPTY,  // EMPTY is a special type for empty menus that is only used
+            // internally.
   };
 
   // Where the menu should be drawn, above or below the bounds (when
@@ -257,6 +258,15 @@
   // Returns true if the item is selected.
   bool IsSelected() const { return selected_; }
 
+  // Sets whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+  void SetSelectionOfActionableSubmenu(
+      bool submenu_area_of_actionable_submenu_selected);
+
+  // Whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+  bool IsSubmenuAreaOfActionableSubmenuSelected() const {
+    return submenu_area_of_actionable_submenu_selected_;
+  }
+
   // Sets the |tooltip| for a menu item view with |item_id| identifier.
   void SetTooltip(const base::string16& tooltip, int item_id);
 
@@ -288,6 +298,9 @@
   // dimensions.
   int GetHeightForWidth(int width) const override;
 
+  // Returns the bounds of the submenu part of the ACTIONABLE_SUBMENU.
+  gfx::Rect GetSubmenuAreaOfActionableSubmenu() const;
+
   // Return the preferred dimensions of the item in pixel.
   const MenuItemDimensions& GetDimensions() const;
 
@@ -492,6 +505,9 @@
   // Whether we're selected.
   bool selected_;
 
+  // Whether the submenu area of an ACTIONABLE_SUBMENU is selected.
+  bool submenu_area_of_actionable_submenu_selected_;
+
   // Command id.
   int command_;
 
@@ -573,6 +589,10 @@
   // The forced visual selection state of this item, if any.
   base::Optional<bool> forced_visual_selection_;
 
+  // The vertical separator that separates the actionable and submenu regions of
+  // an ACTIONABLE_SUBMENU.
+  Separator* vertical_separator_;
+
   DISALLOW_COPY_AND_ASSIGN(MenuItemView);
 };
 
diff --git a/ui/views/controls/menu/menu_item_view_unittest.cc b/ui/views/controls/menu/menu_item_view_unittest.cc
index 9170ded6..07273f5d 100644
--- a/ui/views/controls/menu/menu_item_view_unittest.cc
+++ b/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -41,6 +41,8 @@
 
   void AddEmptyMenus() { MenuItemView::AddEmptyMenus(); }
 
+  void SetHasMnemonics(bool has_mnemonics) { has_mnemonics_ = has_mnemonics; }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
 };
@@ -146,6 +148,24 @@
             empty_item->title());
 }
 
+TEST(MenuItemViewUnitTest, UseMnemonicOnPlatform) {
+  TestMenuItemView root_menu;
+  views::MenuItemView* item1 =
+      root_menu.AppendMenuItemWithLabel(1, base::ASCIIToUTF16("&Item 1"));
+  views::MenuItemView* item2 =
+      root_menu.AppendMenuItemWithLabel(2, base::ASCIIToUTF16("I&tem 2"));
+
+  root_menu.SetHasMnemonics(true);
+
+  if (MenuConfig::instance().use_mnemonics) {
+    EXPECT_EQ('i', item1->GetMnemonic());
+    EXPECT_EQ('t', item2->GetMnemonic());
+  } else {
+    EXPECT_EQ(0, item1->GetMnemonic());
+    EXPECT_EQ(0, item2->GetMnemonic());
+  }
+}
+
 class MenuItemViewPaintUnitTest : public ViewsTestBase {
  public:
   MenuItemViewPaintUnitTest() {}
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index e1a2339..e9cfe1c 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -80,6 +80,9 @@
     case ui::MenuModel::TYPE_SUBMENU:
       type = MenuItemView::SUBMENU;
       break;
+    case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+      type = MenuItemView::ACTIONABLE_SUBMENU;
+      break;
   }
 
   if (*type == MenuItemView::SEPARATOR) {
@@ -267,9 +270,11 @@
   for (int i = 0; i < item_count; ++i) {
     MenuItemView* item = AppendMenuItem(menu, model, i);
 
-    if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
+    if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU ||
+        model->GetTypeAt(i) == ui::MenuModel::TYPE_ACTIONABLE_SUBMENU) {
       DCHECK(item);
-      DCHECK_EQ(MenuItemView::SUBMENU, item->GetType());
+      DCHECK(item->GetType() == MenuItemView::SUBMENU ||
+             item->GetType() == MenuItemView::ACTIONABLE_SUBMENU);
       ui::MenuModel* submodel = model->GetSubmenuModelAt(i);
       DCHECK(submodel);
       BuildMenuImpl(item, submodel);
diff --git a/ui/views/controls/menu/menu_model_adapter_unittest.cc b/ui/views/controls/menu/menu_model_adapter_unittest.cc
index c5df954..df3bb3e 100644
--- a/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -16,8 +16,9 @@
 namespace {
 
 // Base command id for test menu and its submenu.
-const int kRootIdBase = 100;
-const int kSubmenuIdBase = 200;
+constexpr int kRootIdBase = 100;
+constexpr int kSubmenuIdBase = 200;
+constexpr int kActionableSubmenuIdBase = 300;
 
 class MenuModelBase : public ui::MenuModel {
  public:
@@ -139,26 +140,98 @@
   DISALLOW_COPY_AND_ASSIGN(SubmenuModel);
 };
 
+class ActionableSubmenuModel : public MenuModelBase {
+ public:
+  ActionableSubmenuModel() : MenuModelBase(kActionableSubmenuIdBase) {
+    items_.push_back(Item(TYPE_COMMAND, "actionable submenu item 0", NULL));
+    items_.push_back(Item(TYPE_COMMAND, "actionable submenu item 1", NULL));
+  }
+  ~ActionableSubmenuModel() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ActionableSubmenuModel);
+};
+
 class RootModel : public MenuModelBase {
  public:
   RootModel() : MenuModelBase(kRootIdBase) {
-    submenu_model_.reset(new SubmenuModel);
+    submenu_model_ = std::make_unique<SubmenuModel>();
+    actionable_submenu_model_ = std::make_unique<ActionableSubmenuModel>();
 
     items_.push_back(Item(TYPE_COMMAND, "command 0", NULL));
     items_.push_back(Item(TYPE_CHECK, "check 1", NULL));
     items_.push_back(Item(TYPE_SEPARATOR, "", NULL));
     items_.push_back(Item(TYPE_SUBMENU, "submenu 3", submenu_model_.get()));
     items_.push_back(Item(TYPE_RADIO, "radio 4", NULL));
+    items_.push_back(Item(TYPE_ACTIONABLE_SUBMENU, "actionable 5",
+                          actionable_submenu_model_.get()));
   }
 
   ~RootModel() override {}
 
  private:
   std::unique_ptr<MenuModel> submenu_model_;
+  std::unique_ptr<MenuModel> actionable_submenu_model_;
 
   DISALLOW_COPY_AND_ASSIGN(RootModel);
 };
 
+void CheckSubmenu(const RootModel& model,
+                  views::MenuItemView* menu,
+                  views::MenuModelAdapter* delegate,
+                  int submenu_id,
+                  int expected_children,
+                  int submenu_model_index,
+                  int id_base) {
+  views::MenuItemView* submenu = menu->GetMenuItemByID(submenu_id);
+  views::SubmenuView* subitem_container = submenu->GetSubmenu();
+  EXPECT_EQ(expected_children, subitem_container->child_count());
+
+  for (int i = 0; i < subitem_container->child_count(); ++i) {
+    MenuModelBase* submodel = static_cast<MenuModelBase*>(
+        model.GetSubmenuModelAt(submenu_model_index));
+    EXPECT_TRUE(submodel);
+
+    const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
+
+    const int id = i + id_base;
+    views::MenuItemView* item = menu->GetMenuItemByID(id);
+    if (!item) {
+      EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
+      continue;
+    }
+    // Check placement.
+    EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
+
+    // Check type.
+    switch (model_item.type) {
+      case ui::MenuModel::TYPE_COMMAND:
+        EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
+        break;
+      case ui::MenuModel::TYPE_CHECK:
+        EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
+        break;
+      case ui::MenuModel::TYPE_RADIO:
+        EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
+        break;
+      case ui::MenuModel::TYPE_SEPARATOR:
+      case ui::MenuModel::TYPE_BUTTON_ITEM:
+        break;
+      case ui::MenuModel::TYPE_SUBMENU:
+        EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
+        break;
+      case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+        EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
+        break;
+    }
+
+    // Check activation.
+    static_cast<views::MenuDelegate*>(delegate)->ExecuteCommand(id);
+    EXPECT_EQ(i, submodel->last_activation());
+    submodel->set_last_activation(-1);
+  }
+}
+
 }  // namespace
 
 namespace views {
@@ -180,7 +253,7 @@
 
   // Check top level menu items.
   views::SubmenuView* item_container = menu->GetSubmenu();
-  EXPECT_EQ(5, item_container->child_count());
+  EXPECT_EQ(6, item_container->child_count());
 
   for (int i = 0; i < item_container->child_count(); ++i) {
     const MenuModelBase::Item& model_item = model.GetItemDefinition(i);
@@ -212,6 +285,9 @@
       case ui::MenuModel::TYPE_SUBMENU:
         EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
         break;
+      case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
+        EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
+        break;
     }
 
     // Check activation.
@@ -220,52 +296,15 @@
     model.set_last_activation(-1);
   }
 
-  // Check submenu items.
-  views::MenuItemView* submenu = menu->GetMenuItemByID(103);
-  views::SubmenuView* subitem_container = submenu->GetSubmenu();
-  EXPECT_EQ(2, subitem_container->child_count());
+  // Check the submenu.
+  const int submenu_index = 3;
+  CheckSubmenu(model, menu, &delegate, kRootIdBase + submenu_index, 2,
+               submenu_index, kSubmenuIdBase);
 
-  for (int i = 0; i < subitem_container->child_count(); ++i) {
-    MenuModelBase* submodel = static_cast<MenuModelBase*>(
-        model.GetSubmenuModelAt(3));
-    EXPECT_TRUE(submodel);
-
-    const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
-
-    const int id = i + kSubmenuIdBase;
-    MenuItemView* item = menu->GetMenuItemByID(id);
-    if (!item) {
-      EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
-      continue;
-    }
-
-    // Check placement.
-    EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
-
-    // Check type.
-    switch (model_item.type) {
-      case ui::MenuModel::TYPE_COMMAND:
-        EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
-        break;
-      case ui::MenuModel::TYPE_CHECK:
-        EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
-        break;
-      case ui::MenuModel::TYPE_RADIO:
-        EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
-        break;
-      case ui::MenuModel::TYPE_SEPARATOR:
-      case ui::MenuModel::TYPE_BUTTON_ITEM:
-        break;
-      case ui::MenuModel::TYPE_SUBMENU:
-        EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
-        break;
-    }
-
-    // Check activation.
-    static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
-    EXPECT_EQ(i, submodel->last_activation());
-    submodel->set_last_activation(-1);
-  }
+  // Check the actionable submenu.
+  const int actionable_submenu_index = 5;
+  CheckSubmenu(model, menu, &delegate, kRootIdBase + actionable_submenu_index,
+               2, actionable_submenu_index, kActionableSubmenuIdBase);
 
   // Check that selecting the root item is safe.  The MenuModel does
   // not care about the root so MenuModelAdapter should do nothing
diff --git a/ui/views/controls/menu/menu_types.h b/ui/views/controls/menu/menu_types.h
index d95ce0b..03a0c72 100644
--- a/ui/views/controls/menu/menu_types.h
+++ b/ui/views/controls/menu/menu_types.h
@@ -9,8 +9,7 @@
 
 // Where a popup menu should be anchored to for non-RTL languages. The opposite
 // position will be used if base::i18n:IsRTL() is true. The BUBBLE flags are
-// used when the menu should get enclosed by a bubble. Note that BUBBLE flags
-// should only be used with menus which have no children. The Fixed flags are
+// used when the menu should get enclosed by a bubble. The Fixed flags are
 // used for the menus that have a fixed anchor position.
 enum MenuAnchorPosition {
   MENU_ANCHOR_TOPLEFT,
diff --git a/ui/views/controls/menu/submenu_view.cc b/ui/views/controls/menu/submenu_view.cc
index 9b244c9..b69a318 100644
--- a/ui/views/controls/menu/submenu_view.cc
+++ b/ui/views/controls/menu/submenu_view.cc
@@ -182,8 +182,9 @@
                                 minimum_preferred_width_ - 2 * insets.width()));
 
   if (GetMenuItem()->GetMenuController() &&
-      GetMenuItem()->GetMenuController()->use_touchable_layout())
+      GetMenuItem()->GetMenuController()->use_touchable_layout()) {
     width = std::max(touchable_minimum_width, width);
+  }
 
   // Then, the height for that width.
   int height = 0;
diff --git a/ui/views/controls/views_text_services_context_menu_mac.mm b/ui/views/controls/views_text_services_context_menu_mac.mm
index 0c4cd26..81015952 100644
--- a/ui/views/controls/views_text_services_context_menu_mac.mm
+++ b/ui/views/controls/views_text_services_context_menu_mac.mm
@@ -8,6 +8,7 @@
 
 #include "base/feature_list.h"
 #include "ui/base/cocoa/text_services_context_menu.h"
+#include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -43,7 +44,9 @@
           l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, text));
       menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
     }
-    if (base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu)) {
+    // TODO(crbug.com/827404): Move this to the cross-platform context menu
+    // code.
+    if (ui::IsEmojiPanelSupported()) {
       menu->InsertItemWithStringIdAt(index++, IDS_CONTENT_CONTEXT_EMOJI,
                                      IDS_CONTENT_CONTEXT_EMOJI);
       menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
@@ -85,7 +88,7 @@
   void ExecuteCommand(int command_id) override {
     switch (command_id) {
       case IDS_CONTENT_CONTEXT_EMOJI:
-        [NSApp orderFrontCharacterPalette:nil];
+        ui::ShowEmojiPanel();
         break;
 
       case IDS_CONTENT_CONTEXT_LOOK_UP:
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc
index 270d1f5f..ae12a5a 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -698,7 +698,9 @@
   return false;
 }
 
-void DesktopWindowTreeHostMus::FrameTypeChanged() {}
+void DesktopWindowTreeHostMus::FrameTypeChanged() {
+  native_widget_delegate_->AsWidget()->ThemeChanged();
+}
 
 void DesktopWindowTreeHostMus::SetFullscreen(bool fullscreen) {
   if (IsFullscreen() == fullscreen)
diff --git a/ui/views/style/typography_provider.cc b/ui/views/style/typography_provider.cc
index a66e387..e208380 100644
--- a/ui/views/style/typography_provider.cc
+++ b/ui/views/style/typography_provider.cc
@@ -37,12 +37,11 @@
 gfx::Font::Weight TypographyProvider::MediumWeightForUI() {
 #if defined(OS_MACOSX)
   // System fonts are not user-configurable on Mac, so there's a simpler check.
-  // However, 10.9 and 10.11 do not ship with a MEDIUM weight system font.  In
-  // that case, trying to use MEDIUM there will give a bold font, which will
-  // look worse with the surrounding NORMAL text than just using NORMAL.
-  return (base::mac::IsOS10_9() || base::mac::IsOS10_11())
-             ? gfx::Font::Weight::NORMAL
-             : gfx::Font::Weight::MEDIUM;
+  // However, 10.11 do not ship with a MEDIUM weight system font. In that
+  // case, trying to use MEDIUM there will give a bold font, which will look
+  // worse with the surrounding NORMAL text than just using NORMAL.
+  return base::mac::IsOS10_11() ? gfx::Font::Weight::NORMAL
+                                : gfx::Font::Weight::MEDIUM;
 #else
   // NORMAL may already have at least MEDIUM weight. Return NORMAL in that case
   // since trying to return MEDIUM would actually make the font lighter-weight
diff --git a/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html b/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html
index 388faf5d..6483cef 100644
--- a/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html
+++ b/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html
@@ -1,23 +1,18 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="certificate_shared_css.html">
 <link rel="import" href="certificates_browser_proxy.html">
 
 <dom-module id="ca-trust-edit-dialog">
   <template>
-    <style include="certificate-shared paper-button-style paper-checkbox-style">
-      paper-checkbox {
-        display: block;
-      }
-
-      paper-checkbox,
+    <style include="certificate-shared paper-button-style">
+      cr-checkbox,
       #description {
         margin: 15px 0;
       }
@@ -32,15 +27,15 @@
         <div id="description">
           [[i18n('certificateManagerCaTrustEditDialogDescription')]]
         </div>
-        <paper-checkbox id="ssl" checked="[[trustInfo_.ssl]]">
+        <cr-checkbox id="ssl" checked="[[trustInfo_.ssl]]">
           [[i18n('certificateManagerCaTrustEditDialogSsl')]]
-        </paper-checkbox>
-        <paper-checkbox id="email" checked="[[trustInfo_.email]]">
+        </cr-checkbox>
+        <cr-checkbox id="email" checked="[[trustInfo_.email]]">
           [[i18n('certificateManagerCaTrustEditDialogEmail')]]
-        </paper-checkbox>
-        <paper-checkbox id="objSign" checked="[[trustInfo_.objSign]]">
+        </cr-checkbox>
+        <cr-checkbox id="objSign" checked="[[trustInfo_.objSign]]">
           [[i18n('certificateManagerCaTrustEditDialogObjSign')]]
-        </paper-checkbox>
+        </cr-checkbox>
       </div>
       <div slot="button-container">
         <paper-spinner-lite id="spinner"></paper-spinner-lite>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
index 1a2c716e..b9a8be8 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -9,7 +10,7 @@
 
 <dom-module id="network-apnlist">
   <template>
-    <style include="network-shared md-select">
+    <style include="network-shared md-select paper-button-style">
       paper-button {
         margin: 4px 0;
       }
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
index 24afaea..41b6085 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
@@ -9,13 +10,7 @@
 
 <dom-module id="network-choose-mobile">
   <template>
-    <style include="network-shared md-select iron-flex">
-      paper-button {
-        margin: 0;
-      }
-      paper-button[disabled] {
-        background: none;
-      }
+    <style include="network-shared md-select iron-flex paper-button-style">
       /* Align select with button and scan messages */
       select {
         margin-top: 4px;
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config_input.html b/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
index 2ed533d..e579ea8 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config_input.html
@@ -30,9 +30,11 @@
     <div class="control-box">
       <paper-input-container always-float-label>
         <label id="label" slot="label">[[label]]</label>
-        <input is="iron-input" value="{{value::input}}" slot="input"
-            tabindex="1" disabled="[[disabled]]" aria-label$="[[label]]"
-            type="[[getInputType_(password, showPassword)]]">
+        <iron-input slot="input">
+          <input value="{{value::input}}" tabindex="1" disabled="[[disabled]]"
+              aria-label$="[[label]]"
+              type="[[getInputType_(password, showPassword)]]">
+        </iron-input>
       </paper-input-container>
       <div id="iconDiv">
         <template is="dom-if" if="[[password]]">
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
index 4caf352..a62966a 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
@@ -88,9 +88,11 @@
           <div class="property-box single-column two-line">
             <template is="dom-repeat" items="[[nameservers_]]">
               <paper-input-container no-label-float>
-                <input id="nameserver[[index]]" is="iron-input" value="[[item]]"
-                    disabled="[[!canEdit_(editable, nameserversType_)]]"
-                    on-change="onValueChange_" slot="input">
+                <iron-input slot="input">
+                  <input id="nameserver[[index]]" value="[[item]]"
+                      disabled="[[!canEdit_(editable, nameserversType_)]]"
+                      on-change="onValueChange_">
+                </iron-input>
               </paper-input-container>
             </template>
           </div>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_property_list.html b/ui/webui/resources/cr_components/chromeos/network/network_property_list.html
index dab56078..e24bb7f 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_property_list.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_property_list.html
@@ -49,10 +49,12 @@
         <template is="dom-if" restamp
             if="[[isEditTypeInput_(item, propertyDict, editFieldTypes)]]">
           <paper-input-container no-label-float>
-            <input id="[[item]]" is="iron-input" slot="input"
-                type="[[getEditInputType_(item, editFieldTypes)]]"
-                value="[[getPropertyValue_(item, prefix, propertyDict)]]"
-                on-change="onValueChange_">
+            <iron-input slot="input">
+              <input id="[[item]]"
+                  type="[[getEditInputType_(item, editFieldTypes)]]"
+                  value="[[getPropertyValue_(item, prefix, propertyDict)]]"
+                  on-change="onValueChange_">
+            </iron-input>
           </paper-input-container>
         </template>
         <!-- TODO(stevenjb): Support other types (number, boolean)? -->
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy.html b/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
index 1834e1db..5640402 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
@@ -130,7 +130,9 @@
         </network-proxy-exclusions>
         <div class="layout horizontal">
           <paper-input-container no-label-float class="flex">
-            <input id="proxyExclusion" is="iron-input" slot="input">
+            <iron-input slot="input">
+              <input id="proxyExclusion">
+            </iron-input>
             <iron-a11y-keys keys="enter" slot="add-on"
                 on-keys-pressed="onAddProxyExclusionTap_">
             </iron-a11y-keys>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
index 940a943..188a8ef 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
@@ -35,14 +35,18 @@
     </style>
     <div id="container">
       <div id="label">[[label]]</div>
-      <paper-input-container id="host" no-label-float>
-        <input is="iron-input" bind-value="{{value.Host}}" slot="input"
-            disabled="[[!editable]]" on-change="onValueChange_">
+      <paper-input-container id="host" no-label-float disabled$="[[!editable]]">
+        <iron-input slot="input">
+          <input value="{{value.Host}}" disabled$="[[!editable]]"
+              on-change="onValueChange_">
+        </iron-input>
       </paper-input-container>
       <div>[[i18n('networkProxyPort')]]</div>
-      <paper-input-container id="port" no-label-float>
-        <input is="iron-input" bind-value="{{value.Port}}" slot="input"
-            disabled="[[!editable]]" on-change="onValueChange_">
+      <paper-input-container id="port" no-label-float disabled$="[[!editable]]">
+        <iron-input slot="input">
+          <input value="{{value.Port}}" disabled$="[[!editable]]"
+              on-change="onValueChange_">
+        </iron-input>
       </paper-input-container>
     </div>
   </template>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html b/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html
index 7c2deb9..6377caca 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html
@@ -1,11 +1,10 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/html/md_select_css.html">
 
  <!-- Common styles for network elements. -->
 
 <dom-module id="network-shared">
   <template>
-    <style include="cr-shared-style md-select">
+    <style include="cr-shared-style">
       :root {
         /* Margin for the show/hide password icon */
         --network-control-margin: 40px;
@@ -71,6 +70,10 @@
         margin-bottom: 0;
         margin-top: -9px;
       }
+
+      iron-input input {
+        @apply --paper-input-container-shared-input-style;
+      }
     </style>
   </template>
 </dom-module>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
index 0cff8dd8..57cc81e 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
@@ -16,7 +17,7 @@
 
 <dom-module id="network-siminfo">
   <template>
-    <style include="network-shared iron-flex">
+    <style include="network-shared iron-flex paper-button-style">
       :host {
         cursor: default
       }
@@ -132,7 +133,8 @@
         </div>
       </div>
       <div slot="button-container">
-        <paper-button on-tap="sendChangePin_" disabled="[[inProgress_]]">
+        <paper-button class="action-button" on-tap="sendChangePin_"
+            disabled="[[inProgress_]]">
           [[i18n('networkSimChange')]]
         </paper-button>
       </div>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index 5e069a548..03b2358 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -172,7 +172,7 @@
     <div id="root" on-contextmenu="onContextMenu_" on-tap="focusInput_">
       <div id="pinInputDiv" class="row">
         <paper-input id="pinInput" type="password" no-label-float
-            value="[[value]]"
+            value="{{value}}"
             is-input-rtl$="[[isInputRtl_(value)]]"
             has-content$="[[hasInput_(value)]]"
             label="[[getInputPlaceholder_(enablePassword)]]"
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
index 8ffadaa..66f79182 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
@@ -71,13 +71,7 @@
      * @type {?Element}
      * @private
      */
-    passwordElement: {
-      type: Object,
-      value: function() {
-        return this.$.pinInput.inputElement;
-      },
-      observer: 'onPasswordElementAttached_',
-    },
+    passwordElement: Object,
 
     /**
      * The intervalID used for the backspace button set/clear interval.
@@ -98,15 +92,6 @@
     },
 
     /**
-     * Whether or not to show the default pin input.
-     * @private
-     */
-    showPinInput_: {
-      type: Boolean,
-      value: false,
-    },
-
-    /**
      * The value stored in the keyboard's input element.
      * @private
      */
@@ -119,32 +104,12 @@
   },
 
   /**
-   * Called when a password element is attached to the pin keyboard.
-   * @param {HTMLInputElement} inputElement The PIN keyboard's input element.
-   * @private
-   */
-  onPasswordElementAttached_: function(inputElement) {
-    this.showPinInput_ = inputElement == this.$.pinInput.inputElement;
-    inputElement.addEventListener('input', this.handleInputChanged_.bind(this));
-  },
-
-  /**
-   * Called when the user uses the keyboard to enter a value into the input
-   * element.
-   * @param {Event} event The event object.
-   * @private
-   */
-  handleInputChanged_: function(event) {
-    this.value = event.target.value;
-  },
-
-  /**
    * Gets the selection start of the input field.
    * @type {number}
    * @private
    */
   get selectionStart_() {
-    return this.passwordElement.selectionStart;
+    return this.passwordElement_().selectionStart;
   },
 
   /**
@@ -153,7 +118,7 @@
    * @private
    */
   get selectionEnd_() {
-    return this.passwordElement.selectionEnd;
+    return this.passwordElement_().selectionEnd;
   },
 
   /**
@@ -162,7 +127,7 @@
    * @private
    */
   set selectionStart_(start) {
-    this.passwordElement.selectionStart = start;
+    this.passwordElement_().selectionStart = start;
   },
 
   /**
@@ -171,14 +136,14 @@
    * @private
    */
   set selectionEnd_(end) {
-    this.passwordElement.selectionEnd = end;
+    this.passwordElement_().selectionEnd = end;
   },
 
   /**
    * Transfers blur to the input element.
    */
   blur: function() {
-    this.passwordElement.blur();
+    this.passwordElement_().blur();
   },
 
   /**
@@ -190,7 +155,7 @@
    */
   focus: function(opt_selectionStart, opt_selectionEnd) {
     setTimeout(function() {
-      this.passwordElement.focus();
+      this.passwordElement_().focus();
       this.selectionStart_ = opt_selectionStart || 0;
       this.selectionEnd_ = opt_selectionEnd || 0;
     }.bind(this), 0);
@@ -242,10 +207,9 @@
    * @param {string} previous
    */
   onPinValueChange_: function(value, previous) {
-    if (value != previous) {
-      this.passwordElement.value = this.value;
-      this.fire('pin-change', {pin: value});
-    }
+    if (this.passwordElement)
+      this.passwordElement.value = value;
+    this.fire('pin-change', {pin: value});
   },
 
   /**
@@ -423,5 +387,15 @@
     e.preventDefault();
     e.stopPropagation();
   },
+
+  /**
+   * @return {!HTMLElement} Returns the native input element of |pinInput|.
+   * @private
+   */
+  passwordElement_: function() {
+    // |passwordElement| is null by default. It can be set to override the
+    // input field that will be populated with the keypad.
+    return this.passwordElement || this.$.pinInput.inputElement.inputElement;
+  },
 });
 })();
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
index ef6cca6b2..3d74601 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
@@ -43,7 +43,7 @@
       }
 
       :host ::slotted(.dropdown-item[disabled]) {
-        opacity: 0.65;
+        opacity: var(--cr-action-menu-disabled-item-opacity, 0.65);
       }
 
       :host ::slotted(.dropdown-item:not([disabled])) {
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index be77b46..c1d83b1 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -3,7 +3,19 @@
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<!--
+List of customizable styles:
 
+  --cr-checkbox-border-size
+  --cr-checkbox-checked-box-color
+  --cr-checkbox-label-container (CSS mixin)
+  --cr-checkbox-mark-color
+  --cr-checkbox-ripple-checked-color
+  --cr-checkbox-ripple-size
+  --cr-checkbox-ripple-unchecked-color
+  --cr-checkbox-size
+  --cr-checkbox-unchecked-box-color
+-->
 <dom-module id="cr-checkbox">
   <template>
     <style>
@@ -14,6 +26,12 @@
         display: flex;
         outline: none;
         user-select: none;
+
+        --cr-checkbox-size: 16px;
+        --cr-checkbox-border-size: 2px;
+        --cr-checkbox-ripple-size: 40px;
+        --cr-checkbox-ripple-offset: calc(var(--cr-checkbox-size)/2 -
+            var(--cr-checkbox-ripple-size)/2 - var(--cr-checkbox-border-size));
       }
 
       :host([disabled]) {
@@ -24,48 +42,46 @@
 
       #checkbox {
         background: none;
-        border: none;
+        border: var(--cr-checkbox-border-size) solid
+            var(--cr-checkbox-unchecked-box-color, var(--google-grey-700));
+        border-radius: 2px;
+        box-sizing: border-box;
         cursor: pointer;
-        height: 16px;
+        display: block;
+        flex-shrink: 0;
+        height: var(--cr-checkbox-size);
         margin: 0;
         outline: none;
         padding: 0;
         position: relative;
         transform: none;  /* Checkboxes shouldn't flip even in RTL. */
-        width: 16px;
+        width: var(--cr-checkbox-size);
       }
 
       #checkmark {
-        border: 2px solid var(--google-grey-700);
-        border-radius: 2px;
-        display: block;
-        height: 12px;
-        width: 12px;
-      }
-
-      #checkmark::after {
-        border-color: inherit;
+        border-color: var(--cr-checkbox-mark-color, white);
         border-style: solid;
         border-width: 0 2px 2px 0;
         content: '';
         display: block;
         height: 70%;
         transform: scale(0) rotate(45deg);
-        transform-origin: 97% 86%;
+        transform-origin: 100% 85%;
         width: 36%;
       }
 
-      :host-context([dir='rtl']) #checkmark::after {
+      :host-context([dir='rtl']) #checkmark {
         transform-origin: 50% 14%;
       }
 
-      :host([checked]) #checkmark {
-        background: var(--google-blue-600);
-        border-color: var(--google-blue-600);
+      :host([checked]) #checkbox {
+        background: var(--cr-checkbox-checked-box-color,
+            var(--google-blue-600));
+        border-color: var(--cr-checkbox-checked-box-color,
+            var(--google-blue-600));
       }
 
-      :host([checked]) #checkmark::after {
-        border-color: white;
+      :host([checked]) #checkmark {
         transform: scale(1) rotate(45deg);
         /* Only animate when showing checkmark. */
         transition: transform 140ms ease-out;
@@ -73,26 +89,29 @@
 
       paper-ripple {
         --paper-ripple-opacity: 0.1;
-        color: var(--google-grey-900);
-        height: 40px;
-        left: -12px;
+        color: var(--cr-checkbox-ripple-unchecked-color, var(--google-grey-900));
+        height: var(--cr-checkbox-ripple-size);
+        left: var(--cr-checkbox-ripple-offset);
         pointer-events: none;
-        top: -12px;
+        top: var(--cr-checkbox-ripple-offset);
         transition: color linear 80ms;
-        width: 40px;
+        width: var(--cr-checkbox-ripple-size);
       }
 
       :host([checked]) paper-ripple {
-        color: var(--google-blue-600);
+        color: var(--cr-checkbox-ripple-checked-color, var(--google-blue-600));
       }
 
       :host-context([dir=rtl]) paper-ripple {
         left: auto;
-        right: -12px;
+        right: var(--cr-checkbox-ripple-offset);
       }
 
       #label-container {
         -webkit-padding-start: 20px;
+        white-space: normal;
+
+        @apply --cr-checkbox-label-container;
       }
 
       :host(.no-label) #label-container {
diff --git a/ui/webui/resources/cr_elements/paper_checkbox_style_css.html b/ui/webui/resources/cr_elements/paper_checkbox_style_css.html
deleted file mode 100644
index 6aab514..0000000
--- a/ui/webui/resources/cr_elements/paper_checkbox_style_css.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<!-- Common paper-checkbox styling for Material Design WebUI. -->
-<dom-module id="paper-checkbox-style">
-  <template>
-    <style>
-      paper-checkbox {
-        --paper-checkbox-checked-color: var(--google-blue-500);
-        --paper-checkbox-ink-size: 40px;
-        --paper-checkbox-label-color: inherit;
-        --paper-checkbox-label-spacing: 20px;
-        --paper-checkbox-size: 16px;
-        --paper-checkbox-unchecked-color: var(--paper-grey-600);
-        -webkit-margin-start: 2px;
-      }
-    </style>
-  </template>
-</dom-module>
-
diff --git a/ui/webui/resources/cr_elements/paper_input_style_css.html b/ui/webui/resources/cr_elements/paper_input_style_css.html
index 60009fd7..4ea1c98 100644
--- a/ui/webui/resources/cr_elements/paper_input_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_input_style_css.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
-<!-- Common paper-checkbox styling for Material Design WebUI. -->
+<!-- Common paper-input styling for Material Design WebUI. -->
 <dom-module id="paper-input-style">
   <template>
     <style>
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index 21aadf1..c03e4894 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -280,10 +280,6 @@
              file="../../webui/resources/cr_elements/paper_button_style_css.html"
              type="chrome_html"
              compress="gzip" />
-  <structure name="IDR_CR_ELEMENTS_PAPER_CHECKBOX_STYLE_CSS_HTML"
-             file="../../webui/resources/cr_elements/paper_checkbox_style_css.html"
-             type="chrome_html"
-             compress="gzip" />
   <structure name="IDR_CR_ELEMENTS_PAPER_INPUT_STYLE_CSS_HTML"
              file="../../webui/resources/cr_elements/paper_input_style_css.html"
              type="chrome_html"